1 //===----------------------------------------------------------------------===// 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/MemRef/IR/MemRef.h" 10 #include "mlir/Dialect/MemRef/Utils/MemRefUtils.h" 11 #include "mlir/Dialect/StandardOps/IR/Ops.h" 12 #include "mlir/Dialect/StandardOps/Utils/Utils.h" 13 #include "mlir/Dialect/Tensor/IR/Tensor.h" 14 #include "mlir/IR/AffineMap.h" 15 #include "mlir/IR/Builders.h" 16 #include "mlir/IR/BuiltinTypes.h" 17 #include "mlir/IR/Matchers.h" 18 #include "mlir/IR/PatternMatch.h" 19 #include "mlir/IR/TypeUtilities.h" 20 #include "mlir/Interfaces/InferTypeOpInterface.h" 21 #include "mlir/Interfaces/ViewLikeInterface.h" 22 #include "llvm/ADT/STLExtras.h" 23 24 using namespace mlir; 25 using namespace mlir::memref; 26 27 /// Materialize a single constant operation from a given attribute value with 28 /// the desired resultant type. 29 Operation *MemRefDialect::materializeConstant(OpBuilder &builder, 30 Attribute value, Type type, 31 Location loc) { 32 return builder.create<mlir::ConstantOp>(loc, type, value); 33 } 34 35 /// Extract int64_t values from the assumed ArrayAttr of IntegerAttr. 36 static SmallVector<int64_t, 4> extractFromI64ArrayAttr(Attribute attr) { 37 return llvm::to_vector<4>( 38 llvm::map_range(attr.cast<ArrayAttr>(), [](Attribute a) -> int64_t { 39 return a.cast<IntegerAttr>().getInt(); 40 })); 41 } 42 43 /// Helper function to dispatch an OpFoldResult into either the `dynamicVec` if 44 /// it is a Value or into `staticVec` if it is an IntegerAttr. 45 /// In the case of a Value, a copy of the `sentinel` value is also pushed to 46 /// `staticVec`. This is useful to extract mixed static and dynamic entries that 47 /// come from an AttrSizedOperandSegments trait. 48 static void dispatchIndexOpFoldResult(OpFoldResult ofr, 49 SmallVectorImpl<Value> &dynamicVec, 50 SmallVectorImpl<int64_t> &staticVec, 51 int64_t sentinel) { 52 if (auto v = ofr.dyn_cast<Value>()) { 53 dynamicVec.push_back(v); 54 staticVec.push_back(sentinel); 55 return; 56 } 57 APInt apInt = ofr.dyn_cast<Attribute>().cast<IntegerAttr>().getValue(); 58 staticVec.push_back(apInt.getSExtValue()); 59 } 60 61 static void dispatchIndexOpFoldResults(ArrayRef<OpFoldResult> ofrs, 62 SmallVectorImpl<Value> &dynamicVec, 63 SmallVectorImpl<int64_t> &staticVec, 64 int64_t sentinel) { 65 for (auto ofr : ofrs) 66 dispatchIndexOpFoldResult(ofr, dynamicVec, staticVec, sentinel); 67 } 68 69 //===----------------------------------------------------------------------===// 70 // Common canonicalization pattern support logic 71 //===----------------------------------------------------------------------===// 72 73 /// This is a common class used for patterns of the form 74 /// "someop(memrefcast) -> someop". It folds the source of any memref.cast 75 /// into the root operation directly. 76 static LogicalResult foldMemRefCast(Operation *op, Value inner = nullptr) { 77 bool folded = false; 78 for (OpOperand &operand : op->getOpOperands()) { 79 auto cast = operand.get().getDefiningOp<CastOp>(); 80 if (cast && operand.get() != inner && 81 !cast.getOperand().getType().isa<UnrankedMemRefType>()) { 82 operand.set(cast.getOperand()); 83 folded = true; 84 } 85 } 86 return success(folded); 87 } 88 89 //===----------------------------------------------------------------------===// 90 // Helpers for GlobalOp 91 //===----------------------------------------------------------------------===// 92 93 static Type getTensorTypeFromMemRefType(Type type) { 94 if (auto memref = type.dyn_cast<MemRefType>()) 95 return RankedTensorType::get(memref.getShape(), memref.getElementType()); 96 if (auto memref = type.dyn_cast<UnrankedMemRefType>()) 97 return UnrankedTensorType::get(memref.getElementType()); 98 return NoneType::get(type.getContext()); 99 } 100 101 //===----------------------------------------------------------------------===// 102 // AllocOp / AllocaOp 103 //===----------------------------------------------------------------------===// 104 105 template <typename AllocLikeOp> 106 static LogicalResult verifyAllocLikeOp(AllocLikeOp op) { 107 static_assert(llvm::is_one_of<AllocLikeOp, AllocOp, AllocaOp>::value, 108 "applies to only alloc or alloca"); 109 auto memRefType = op.getResult().getType().template dyn_cast<MemRefType>(); 110 if (!memRefType) 111 return op.emitOpError("result must be a memref"); 112 113 if (static_cast<int64_t>(op.dynamicSizes().size()) != 114 memRefType.getNumDynamicDims()) 115 return op.emitOpError("dimension operand count does not equal memref " 116 "dynamic dimension count"); 117 118 unsigned numSymbols = 0; 119 if (!memRefType.getAffineMaps().empty()) 120 numSymbols = memRefType.getAffineMaps().front().getNumSymbols(); 121 if (op.symbolOperands().size() != numSymbols) 122 return op.emitOpError( 123 "symbol operand count does not equal memref symbol count"); 124 125 return success(); 126 } 127 128 static LogicalResult verify(AllocOp op) { return verifyAllocLikeOp(op); } 129 130 static LogicalResult verify(AllocaOp op) { 131 // An alloca op needs to have an ancestor with an allocation scope trait. 132 if (!op->getParentWithTrait<OpTrait::AutomaticAllocationScope>()) 133 return op.emitOpError( 134 "requires an ancestor op with AutomaticAllocationScope trait"); 135 136 return verifyAllocLikeOp(op); 137 } 138 139 namespace { 140 /// Fold constant dimensions into an alloc like operation. 141 template <typename AllocLikeOp> 142 struct SimplifyAllocConst : public OpRewritePattern<AllocLikeOp> { 143 using OpRewritePattern<AllocLikeOp>::OpRewritePattern; 144 145 LogicalResult matchAndRewrite(AllocLikeOp alloc, 146 PatternRewriter &rewriter) const override { 147 // Check to see if any dimensions operands are constants. If so, we can 148 // substitute and drop them. 149 if (llvm::none_of(alloc.getOperands(), [](Value operand) { 150 return matchPattern(operand, matchConstantIndex()); 151 })) 152 return failure(); 153 154 auto memrefType = alloc.getType(); 155 156 // Ok, we have one or more constant operands. Collect the non-constant ones 157 // and keep track of the resultant memref type to build. 158 SmallVector<int64_t, 4> newShapeConstants; 159 newShapeConstants.reserve(memrefType.getRank()); 160 SmallVector<Value, 4> newOperands; 161 162 unsigned dynamicDimPos = 0; 163 for (unsigned dim = 0, e = memrefType.getRank(); dim < e; ++dim) { 164 int64_t dimSize = memrefType.getDimSize(dim); 165 // If this is already static dimension, keep it. 166 if (dimSize != -1) { 167 newShapeConstants.push_back(dimSize); 168 continue; 169 } 170 auto *defOp = alloc.getOperand(dynamicDimPos).getDefiningOp(); 171 if (auto constantIndexOp = dyn_cast_or_null<ConstantIndexOp>(defOp)) { 172 // Dynamic shape dimension will be folded. 173 newShapeConstants.push_back(constantIndexOp.getValue()); 174 } else { 175 // Dynamic shape dimension not folded; copy operand from old memref. 176 newShapeConstants.push_back(-1); 177 newOperands.push_back(alloc.getOperand(dynamicDimPos)); 178 } 179 dynamicDimPos++; 180 } 181 182 // Create new memref type (which will have fewer dynamic dimensions). 183 MemRefType newMemRefType = 184 MemRefType::Builder(memrefType).setShape(newShapeConstants); 185 assert(static_cast<int64_t>(newOperands.size()) == 186 newMemRefType.getNumDynamicDims()); 187 188 // Create and insert the alloc op for the new memref. 189 auto newAlloc = rewriter.create<AllocLikeOp>( 190 alloc.getLoc(), newMemRefType, newOperands, alloc.alignmentAttr()); 191 // Insert a cast so we have the same type as the old alloc. 192 auto resultCast = 193 rewriter.create<CastOp>(alloc.getLoc(), newAlloc, alloc.getType()); 194 195 rewriter.replaceOp(alloc, {resultCast}); 196 return success(); 197 } 198 }; 199 200 /// Fold alloc operations with no users or only store and dealloc uses. 201 template <typename T> 202 struct SimplifyDeadAlloc : public OpRewritePattern<T> { 203 using OpRewritePattern<T>::OpRewritePattern; 204 205 LogicalResult matchAndRewrite(T alloc, 206 PatternRewriter &rewriter) const override { 207 if (llvm::any_of(alloc->getUsers(), [](Operation *op) { 208 return !isa<StoreOp, DeallocOp>(op); 209 })) 210 return failure(); 211 212 for (Operation *user : llvm::make_early_inc_range(alloc->getUsers())) 213 rewriter.eraseOp(user); 214 215 rewriter.eraseOp(alloc); 216 return success(); 217 } 218 }; 219 } // end anonymous namespace. 220 221 void AllocOp::getCanonicalizationPatterns(RewritePatternSet &results, 222 MLIRContext *context) { 223 results.add<SimplifyAllocConst<AllocOp>, SimplifyDeadAlloc<AllocOp>>(context); 224 } 225 226 void AllocaOp::getCanonicalizationPatterns(RewritePatternSet &results, 227 MLIRContext *context) { 228 results.add<SimplifyAllocConst<AllocaOp>, SimplifyDeadAlloc<AllocaOp>>( 229 context); 230 } 231 232 //===----------------------------------------------------------------------===// 233 // AssumeAlignmentOp 234 //===----------------------------------------------------------------------===// 235 236 static LogicalResult verify(AssumeAlignmentOp op) { 237 unsigned alignment = op.alignment(); 238 if (!llvm::isPowerOf2_32(alignment)) 239 return op.emitOpError("alignment must be power of 2"); 240 return success(); 241 } 242 243 //===----------------------------------------------------------------------===// 244 // BufferCastOp 245 //===----------------------------------------------------------------------===// 246 247 OpFoldResult BufferCastOp::fold(ArrayRef<Attribute>) { 248 if (auto tensorLoad = tensor().getDefiningOp<TensorLoadOp>()) 249 if (tensorLoad.memref().getType() == getType()) 250 return tensorLoad.memref(); 251 return {}; 252 } 253 254 namespace { 255 /// Replace tensor_cast + buffer_cast by buffer_cast + memref_cast. 256 struct BufferCast : public OpRewritePattern<BufferCastOp> { 257 using OpRewritePattern<BufferCastOp>::OpRewritePattern; 258 259 LogicalResult matchAndRewrite(BufferCastOp bufferCast, 260 PatternRewriter &rewriter) const final { 261 auto tensorCastOperand = 262 bufferCast.getOperand().getDefiningOp<tensor::CastOp>(); 263 if (!tensorCastOperand) 264 return failure(); 265 auto srcTensorType = 266 tensorCastOperand.getOperand().getType().dyn_cast<RankedTensorType>(); 267 if (!srcTensorType) 268 return failure(); 269 auto memrefType = MemRefType::get(srcTensorType.getShape(), 270 srcTensorType.getElementType()); 271 Value memref = rewriter.create<BufferCastOp>( 272 bufferCast.getLoc(), memrefType, tensorCastOperand.getOperand()); 273 rewriter.replaceOpWithNewOp<CastOp>(bufferCast, bufferCast.getType(), 274 memref); 275 return success(); 276 } 277 }; 278 279 /// Canonicalize memref.tensor_load + memref.buffer_cast to memref.cast when 280 /// type mismatches prevent `BufferCastOp::fold` to kick in. 281 struct TensorLoadToMemRef : public OpRewritePattern<BufferCastOp> { 282 using OpRewritePattern<BufferCastOp>::OpRewritePattern; 283 284 LogicalResult matchAndRewrite(BufferCastOp bufferCast, 285 PatternRewriter &rewriter) const final { 286 auto tensorLoad = bufferCast.tensor().getDefiningOp<TensorLoadOp>(); 287 // Bail unless we have a tensor_load + memref.buffer_cast with different 288 // types. `BufferCastOp::fold` handles the same type case. 289 if (!tensorLoad || tensorLoad.memref().getType() == bufferCast.getType()) 290 return failure(); 291 // If types are not cast-compatible, bail. 292 if (!CastOp::areCastCompatible(tensorLoad.memref().getType(), 293 bufferCast.getType())) 294 return failure(); 295 rewriter.replaceOpWithNewOp<CastOp>(bufferCast, bufferCast.getType(), 296 tensorLoad.memref()); 297 return success(); 298 } 299 }; 300 301 } // namespace 302 303 void BufferCastOp::getCanonicalizationPatterns(RewritePatternSet &results, 304 MLIRContext *context) { 305 results.add<BufferCast, TensorLoadToMemRef>(context); 306 } 307 308 //===----------------------------------------------------------------------===// 309 // CastOp 310 //===----------------------------------------------------------------------===// 311 312 /// Determines whether MemRef_CastOp casts to a more dynamic version of the 313 /// source memref. This is useful to to fold a memref.cast into a consuming op 314 /// and implement canonicalization patterns for ops in different dialects that 315 /// may consume the results of memref.cast operations. Such foldable memref.cast 316 /// operations are typically inserted as `view` and `subview` ops are 317 /// canonicalized, to preserve the type compatibility of their uses. 318 /// 319 /// Returns true when all conditions are met: 320 /// 1. source and result are ranked memrefs with strided semantics and same 321 /// element type and rank. 322 /// 2. each of the source's size, offset or stride has more static information 323 /// than the corresponding result's size, offset or stride. 324 /// 325 /// Example 1: 326 /// ```mlir 327 /// %1 = memref.cast %0 : memref<8x16xf32> to memref<?x?xf32> 328 /// %2 = consumer %1 ... : memref<?x?xf32> ... 329 /// ``` 330 /// 331 /// may fold into: 332 /// 333 /// ```mlir 334 /// %2 = consumer %0 ... : memref<8x16xf32> ... 335 /// ``` 336 /// 337 /// Example 2: 338 /// ``` 339 /// %1 = memref.cast %0 : memref<?x16xf32, affine_map<(i, j)->(16 * i + j)>> 340 /// to memref<?x?xf32> 341 /// consumer %1 : memref<?x?xf32> ... 342 /// ``` 343 /// 344 /// may fold into: 345 /// 346 /// ``` 347 /// consumer %0 ... : memref<?x16xf32, affine_map<(i, j)->(16 * i + j)>> 348 /// ``` 349 bool CastOp::canFoldIntoConsumerOp(CastOp castOp) { 350 MemRefType sourceType = castOp.source().getType().dyn_cast<MemRefType>(); 351 MemRefType resultType = castOp.getType().dyn_cast<MemRefType>(); 352 353 // Requires ranked MemRefType. 354 if (!sourceType || !resultType) 355 return false; 356 357 // Requires same elemental type. 358 if (sourceType.getElementType() != resultType.getElementType()) 359 return false; 360 361 // Requires same rank. 362 if (sourceType.getRank() != resultType.getRank()) 363 return false; 364 365 // Only fold casts between strided memref forms. 366 int64_t sourceOffset, resultOffset; 367 SmallVector<int64_t, 4> sourceStrides, resultStrides; 368 if (failed(getStridesAndOffset(sourceType, sourceStrides, sourceOffset)) || 369 failed(getStridesAndOffset(resultType, resultStrides, resultOffset))) 370 return false; 371 372 // If cast is towards more static sizes along any dimension, don't fold. 373 for (auto it : llvm::zip(sourceType.getShape(), resultType.getShape())) { 374 auto ss = std::get<0>(it), st = std::get<1>(it); 375 if (ss != st) 376 if (MemRefType::isDynamic(ss) && !MemRefType::isDynamic(st)) 377 return false; 378 } 379 380 // If cast is towards more static offset along any dimension, don't fold. 381 if (sourceOffset != resultOffset) 382 if (MemRefType::isDynamicStrideOrOffset(sourceOffset) && 383 !MemRefType::isDynamicStrideOrOffset(resultOffset)) 384 return false; 385 386 // If cast is towards more static strides along any dimension, don't fold. 387 for (auto it : llvm::zip(sourceStrides, resultStrides)) { 388 auto ss = std::get<0>(it), st = std::get<1>(it); 389 if (ss != st) 390 if (MemRefType::isDynamicStrideOrOffset(ss) && 391 !MemRefType::isDynamicStrideOrOffset(st)) 392 return false; 393 } 394 395 return true; 396 } 397 398 bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) { 399 if (inputs.size() != 1 || outputs.size() != 1) 400 return false; 401 Type a = inputs.front(), b = outputs.front(); 402 auto aT = a.dyn_cast<MemRefType>(); 403 auto bT = b.dyn_cast<MemRefType>(); 404 405 auto uaT = a.dyn_cast<UnrankedMemRefType>(); 406 auto ubT = b.dyn_cast<UnrankedMemRefType>(); 407 408 if (aT && bT) { 409 if (aT.getElementType() != bT.getElementType()) 410 return false; 411 if (aT.getAffineMaps() != bT.getAffineMaps()) { 412 int64_t aOffset, bOffset; 413 SmallVector<int64_t, 4> aStrides, bStrides; 414 if (failed(getStridesAndOffset(aT, aStrides, aOffset)) || 415 failed(getStridesAndOffset(bT, bStrides, bOffset)) || 416 aStrides.size() != bStrides.size()) 417 return false; 418 419 // Strides along a dimension/offset are compatible if the value in the 420 // source memref is static and the value in the target memref is the 421 // same. They are also compatible if either one is dynamic (see 422 // description of MemRefCastOp for details). 423 auto checkCompatible = [](int64_t a, int64_t b) { 424 return (a == MemRefType::getDynamicStrideOrOffset() || 425 b == MemRefType::getDynamicStrideOrOffset() || a == b); 426 }; 427 if (!checkCompatible(aOffset, bOffset)) 428 return false; 429 for (auto aStride : enumerate(aStrides)) 430 if (!checkCompatible(aStride.value(), bStrides[aStride.index()])) 431 return false; 432 } 433 if (aT.getMemorySpace() != bT.getMemorySpace()) 434 return false; 435 436 // They must have the same rank, and any specified dimensions must match. 437 if (aT.getRank() != bT.getRank()) 438 return false; 439 440 for (unsigned i = 0, e = aT.getRank(); i != e; ++i) { 441 int64_t aDim = aT.getDimSize(i), bDim = bT.getDimSize(i); 442 if (aDim != -1 && bDim != -1 && aDim != bDim) 443 return false; 444 } 445 return true; 446 } else { 447 if (!aT && !uaT) 448 return false; 449 if (!bT && !ubT) 450 return false; 451 // Unranked to unranked casting is unsupported 452 if (uaT && ubT) 453 return false; 454 455 auto aEltType = (aT) ? aT.getElementType() : uaT.getElementType(); 456 auto bEltType = (bT) ? bT.getElementType() : ubT.getElementType(); 457 if (aEltType != bEltType) 458 return false; 459 460 auto aMemSpace = (aT) ? aT.getMemorySpace() : uaT.getMemorySpace(); 461 auto bMemSpace = (bT) ? bT.getMemorySpace() : ubT.getMemorySpace(); 462 if (aMemSpace != bMemSpace) 463 return false; 464 465 return true; 466 } 467 468 return false; 469 } 470 471 OpFoldResult CastOp::fold(ArrayRef<Attribute> operands) { 472 return succeeded(foldMemRefCast(*this)) ? getResult() : Value(); 473 } 474 475 //===----------------------------------------------------------------------===// 476 // CloneOp 477 //===----------------------------------------------------------------------===// 478 479 void CloneOp::getEffects( 480 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>> 481 &effects) { 482 effects.emplace_back(MemoryEffects::Read::get(), input(), 483 SideEffects::DefaultResource::get()); 484 effects.emplace_back(MemoryEffects::Write::get(), output(), 485 SideEffects::DefaultResource::get()); 486 } 487 488 namespace { 489 /// Fold Dealloc operations that are deallocating an AllocOp that is only used 490 /// by other Dealloc operations. 491 struct SimplifyClones : public OpRewritePattern<CloneOp> { 492 using OpRewritePattern<CloneOp>::OpRewritePattern; 493 494 LogicalResult matchAndRewrite(CloneOp cloneOp, 495 PatternRewriter &rewriter) const override { 496 if (cloneOp.use_empty()) { 497 rewriter.eraseOp(cloneOp); 498 return success(); 499 } 500 501 Value source = cloneOp.input(); 502 503 // This only finds dealloc operations for the immediate value. It should 504 // also consider aliases. That would also make the safety check below 505 // redundant. 506 Operation *cloneDeallocOp = findDealloc(cloneOp.output()); 507 Operation *sourceDeallocOp = findDealloc(source); 508 509 // If both are deallocated in the same block, their in-block lifetimes 510 // might not fully overlap, so we cannot decide which one to drop. 511 if (cloneDeallocOp && sourceDeallocOp && 512 cloneDeallocOp->getBlock() == sourceDeallocOp->getBlock()) 513 return failure(); 514 515 Block *currentBlock = cloneOp->getBlock(); 516 Operation *redundantDealloc = nullptr; 517 if (cloneDeallocOp && cloneDeallocOp->getBlock() == currentBlock) { 518 redundantDealloc = cloneDeallocOp; 519 } else if (sourceDeallocOp && sourceDeallocOp->getBlock() == currentBlock) { 520 redundantDealloc = sourceDeallocOp; 521 } 522 523 if (!redundantDealloc) 524 return failure(); 525 526 // Safety check that there are no other deallocations inbetween 527 // cloneOp and redundantDealloc, as otherwise we might deallocate an alias 528 // of source before the uses of the clone. With alias information, we could 529 // restrict this to only fail of the dealloc's operand is an alias 530 // of the source. 531 for (Operation *pos = cloneOp->getNextNode(); pos != redundantDealloc; 532 pos = pos->getNextNode()) { 533 auto effectInterface = dyn_cast<MemoryEffectOpInterface>(pos); 534 if (!effectInterface) 535 continue; 536 if (effectInterface.hasEffect<MemoryEffects::Free>()) 537 return failure(); 538 } 539 540 rewriter.replaceOpWithNewOp<memref::CastOp>(cloneOp, cloneOp.getType(), 541 source); 542 rewriter.eraseOp(redundantDealloc); 543 return success(); 544 } 545 }; 546 547 } // end anonymous namespace. 548 549 void CloneOp::getCanonicalizationPatterns(OwningRewritePatternList &results, 550 MLIRContext *context) { 551 results.insert<SimplifyClones>(context); 552 } 553 554 OpFoldResult CloneOp::fold(ArrayRef<Attribute> operands) { 555 return succeeded(foldMemRefCast(*this)) ? getResult() : Value(); 556 } 557 558 //===----------------------------------------------------------------------===// 559 // DeallocOp 560 //===----------------------------------------------------------------------===// 561 562 LogicalResult DeallocOp::fold(ArrayRef<Attribute> cstOperands, 563 SmallVectorImpl<OpFoldResult> &results) { 564 /// dealloc(memrefcast) -> dealloc 565 return foldMemRefCast(*this); 566 } 567 568 //===----------------------------------------------------------------------===// 569 // DimOp 570 //===----------------------------------------------------------------------===// 571 572 void DimOp::build(OpBuilder &builder, OperationState &result, Value memref, 573 int64_t index) { 574 auto loc = result.location; 575 Value indexValue = builder.create<ConstantIndexOp>(loc, index); 576 build(builder, result, memref, indexValue); 577 } 578 579 void DimOp::build(OpBuilder &builder, OperationState &result, Value memref, 580 Value index) { 581 auto indexTy = builder.getIndexType(); 582 build(builder, result, indexTy, memref, index); 583 } 584 585 Optional<int64_t> DimOp::getConstantIndex() { 586 if (auto constantOp = index().getDefiningOp<ConstantOp>()) 587 return constantOp.getValue().cast<IntegerAttr>().getInt(); 588 return {}; 589 } 590 591 static LogicalResult verify(DimOp op) { 592 // Assume unknown index to be in range. 593 Optional<int64_t> index = op.getConstantIndex(); 594 if (!index.hasValue()) 595 return success(); 596 597 // Check that constant index is not knowingly out of range. 598 auto type = op.memrefOrTensor().getType(); 599 if (auto memrefType = type.dyn_cast<MemRefType>()) { 600 if (index.getValue() >= memrefType.getRank()) 601 return op.emitOpError("index is out of range"); 602 } else if (auto tensorType = type.dyn_cast<RankedTensorType>()) { 603 if (index.getValue() >= tensorType.getRank()) 604 return op.emitOpError("index is out of range"); 605 } else if (type.isa<UnrankedMemRefType>() || type.isa<UnrankedTensorType>()) { 606 // Assume index to be in range. 607 } else { 608 llvm_unreachable("expected operand with memref type"); 609 } 610 return success(); 611 } 612 613 OpFoldResult DimOp::fold(ArrayRef<Attribute> operands) { 614 auto index = operands[1].dyn_cast_or_null<IntegerAttr>(); 615 616 // All forms of folding require a known index. 617 if (!index) 618 return {}; 619 620 auto argTy = memrefOrTensor().getType(); 621 // Fold if the shape extent along the given index is known. 622 if (auto shapedTy = argTy.dyn_cast<ShapedType>()) { 623 // Folding for unranked types (UnrankedMemRefType) is not supported. 624 if (!shapedTy.hasRank()) 625 return {}; 626 if (!shapedTy.isDynamicDim(index.getInt())) { 627 Builder builder(getContext()); 628 return builder.getIndexAttr(shapedTy.getShape()[index.getInt()]); 629 } 630 } 631 632 Operation *definingOp = memrefOrTensor().getDefiningOp(); 633 634 // dim(memref.tensor_load(memref)) -> dim(memref) 635 if (auto tensorLoadOp = dyn_cast_or_null<TensorLoadOp>(definingOp)) { 636 setOperand(0, tensorLoadOp.memref()); 637 return getResult(); 638 } 639 640 // Fold dim to the operand of tensor.generate. 641 if (auto fromElements = dyn_cast_or_null<tensor::GenerateOp>(definingOp)) { 642 auto resultType = 643 fromElements.getResult().getType().cast<RankedTensorType>(); 644 // The case where the type encodes the size of the dimension is handled 645 // above. 646 assert(resultType.getShape()[index.getInt()] == 647 RankedTensorType::kDynamicSize); 648 649 // Find the operand of the fromElements that corresponds to this index. 650 auto dynExtents = fromElements.dynamicExtents().begin(); 651 for (auto dim : resultType.getShape().take_front(index.getInt())) 652 if (dim == RankedTensorType::kDynamicSize) 653 dynExtents++; 654 655 return Value{*dynExtents}; 656 } 657 658 // The size at the given index is now known to be a dynamic size. 659 unsigned unsignedIndex = index.getValue().getZExtValue(); 660 661 if (auto subtensor = dyn_cast_or_null<mlir::SubTensorOp>(definingOp)) { 662 assert(subtensor.isDynamicSize(unsignedIndex) && 663 "Expected dynamic subtensor size"); 664 return subtensor.getDynamicSize(unsignedIndex); 665 } 666 667 // Fold dim to the size argument for an `AllocOp`, `ViewOp`, or `SubViewOp`. 668 auto memrefType = argTy.dyn_cast<MemRefType>(); 669 if (!memrefType) 670 return {}; 671 672 if (auto alloc = dyn_cast_or_null<AllocOp>(definingOp)) 673 return *(alloc.getDynamicSizes().begin() + 674 memrefType.getDynamicDimIndex(unsignedIndex)); 675 676 if (auto alloca = dyn_cast_or_null<AllocaOp>(definingOp)) 677 return *(alloca.getDynamicSizes().begin() + 678 memrefType.getDynamicDimIndex(unsignedIndex)); 679 680 if (auto view = dyn_cast_or_null<ViewOp>(definingOp)) 681 return *(view.getDynamicSizes().begin() + 682 memrefType.getDynamicDimIndex(unsignedIndex)); 683 684 if (auto sizeInterface = 685 dyn_cast_or_null<OffsetSizeAndStrideOpInterface>(definingOp)) { 686 assert(sizeInterface.isDynamicSize(unsignedIndex) && 687 "Expected dynamic subview size"); 688 return sizeInterface.getDynamicSize(unsignedIndex); 689 } 690 691 // dim(memrefcast) -> dim 692 if (succeeded(foldMemRefCast(*this))) 693 return getResult(); 694 695 return {}; 696 } 697 698 namespace { 699 /// Fold dim of a memref reshape operation to a load into the reshape's shape 700 /// operand. 701 struct DimOfMemRefReshape : public OpRewritePattern<DimOp> { 702 using OpRewritePattern<DimOp>::OpRewritePattern; 703 704 LogicalResult matchAndRewrite(DimOp dim, 705 PatternRewriter &rewriter) const override { 706 auto reshape = dim.memrefOrTensor().getDefiningOp<ReshapeOp>(); 707 708 if (!reshape) 709 return failure(); 710 711 // Place the load directly after the reshape to ensure that the shape memref 712 // was not mutated. 713 rewriter.setInsertionPointAfter(reshape); 714 rewriter.replaceOpWithNewOp<LoadOp>(dim, reshape.shape(), 715 llvm::makeArrayRef({dim.index()})); 716 return success(); 717 } 718 }; 719 720 /// Fold dim of a dim of a cast into the dim of the source of the tensor cast. 721 template <typename CastOpTy> 722 struct DimOfCastOp : public OpRewritePattern<DimOp> { 723 using OpRewritePattern<DimOp>::OpRewritePattern; 724 725 LogicalResult matchAndRewrite(DimOp dimOp, 726 PatternRewriter &rewriter) const override { 727 auto castOp = dimOp.memrefOrTensor().getDefiningOp<CastOpTy>(); 728 if (!castOp) 729 return failure(); 730 Value newSource = castOp.getOperand(); 731 rewriter.replaceOpWithNewOp<DimOp>(dimOp, newSource, dimOp.index()); 732 return success(); 733 } 734 }; 735 736 /// Helper method to get the `Value` that is the shape of the `resultIdx`-th 737 /// result at dimension `dimIndex` from the `ShapedTypeOpInterface`. 738 /// TODO(ravishankarm): This is better put as a interface utility method 739 /// somewhere, but that would imply the interface will depend on the `tensor` 740 /// dialect. Ideally maybe a utility method in the `tensor` dialect. 741 static Value getResultDimFromShapeInterface(OpBuilder &builder, OpResult result, 742 int64_t dimIndex) { 743 unsigned resultNumber = result.getResultNumber(); 744 auto shapedTypeOp = dyn_cast<InferShapedTypeOpInterface>(result.getOwner()); 745 Location loc = result.getOwner()->getLoc(); 746 if (!shapedTypeOp) 747 return nullptr; 748 749 // The interface exposes two methods, one that returns the shape of all the 750 // results as `Value` and other that returns the shape as a list of 751 // `SmallVector<Value>`. The former takes precedence over the latter. So first 752 // check if the op implements the first interface method or the second, and 753 // get the value to use appropriately. 754 SmallVector<Value> reifiedResultShapes; 755 if (succeeded(shapedTypeOp.reifyReturnTypeShapes( 756 builder, result.getOwner()->getOperands(), reifiedResultShapes))) { 757 if (reifiedResultShapes.size() <= resultNumber) 758 return nullptr; 759 Value resultShape = reifiedResultShapes[resultNumber]; 760 auto resultShapeType = resultShape.getType().dyn_cast<RankedTensorType>(); 761 if (!resultShapeType || !resultShapeType.getElementType().isa<IndexType>()) 762 return nullptr; 763 return builder.create<tensor::ExtractOp>( 764 loc, resultShape, builder.createOrFold<ConstantIndexOp>(loc, dimIndex)); 765 } 766 767 SmallVector<SmallVector<Value>> reifiedResultShapesPerDim; 768 if (failed(shapedTypeOp.reifyReturnTypeShapesPerResultDim( 769 builder, reifiedResultShapesPerDim))) 770 return nullptr; 771 if (reifiedResultShapesPerDim.size() <= resultNumber || 772 reifiedResultShapesPerDim[resultNumber].size() != 773 static_cast<size_t>(result.getType().cast<ShapedType>().getRank())) 774 return nullptr; 775 OpFoldResult valueOrAttr = reifiedResultShapesPerDim[resultNumber][dimIndex]; 776 if (auto attr = valueOrAttr.dyn_cast<Attribute>()) 777 return builder.createOrFold<ConstantIndexOp>( 778 loc, attr.cast<IntegerAttr>().getInt()); 779 return valueOrAttr.get<Value>(); 780 } 781 782 /// Fold dim of an operation that implements the InferShapedTypeOpInterface 783 struct DimOfShapedTypeOpInterface : public OpRewritePattern<DimOp> { 784 using OpRewritePattern<DimOp>::OpRewritePattern; 785 786 LogicalResult matchAndRewrite(DimOp dimOp, 787 PatternRewriter &rewriter) const override { 788 OpResult dimValue = dimOp.memrefOrTensor().dyn_cast<OpResult>(); 789 if (!dimValue) 790 return failure(); 791 auto shapedTypeOp = 792 dyn_cast<InferShapedTypeOpInterface>(dimValue.getOwner()); 793 if (!shapedTypeOp) 794 return failure(); 795 796 Optional<int64_t> dimIndex = dimOp.getConstantIndex(); 797 if (!dimIndex) 798 return failure(); 799 Value replacement = 800 getResultDimFromShapeInterface(rewriter, dimValue, *dimIndex); 801 if (!replacement) 802 return failure(); 803 rewriter.replaceOp(dimOp, replacement); 804 return success(); 805 } 806 }; 807 } // end anonymous namespace. 808 809 void DimOp::getCanonicalizationPatterns(RewritePatternSet &results, 810 MLIRContext *context) { 811 results.add<DimOfMemRefReshape, DimOfCastOp<BufferCastOp>, 812 DimOfCastOp<tensor::CastOp>, DimOfShapedTypeOpInterface>(context); 813 } 814 815 // --------------------------------------------------------------------------- 816 // DmaStartOp 817 // --------------------------------------------------------------------------- 818 819 void DmaStartOp::build(OpBuilder &builder, OperationState &result, 820 Value srcMemRef, ValueRange srcIndices, Value destMemRef, 821 ValueRange destIndices, Value numElements, 822 Value tagMemRef, ValueRange tagIndices, Value stride, 823 Value elementsPerStride) { 824 result.addOperands(srcMemRef); 825 result.addOperands(srcIndices); 826 result.addOperands(destMemRef); 827 result.addOperands(destIndices); 828 result.addOperands({numElements, tagMemRef}); 829 result.addOperands(tagIndices); 830 if (stride) 831 result.addOperands({stride, elementsPerStride}); 832 } 833 834 void DmaStartOp::print(OpAsmPrinter &p) { 835 p << getOperationName() << " " << getSrcMemRef() << '[' << getSrcIndices() 836 << "], " << getDstMemRef() << '[' << getDstIndices() << "], " 837 << getNumElements() << ", " << getTagMemRef() << '[' << getTagIndices() 838 << ']'; 839 if (isStrided()) 840 p << ", " << getStride() << ", " << getNumElementsPerStride(); 841 842 p.printOptionalAttrDict((*this)->getAttrs()); 843 p << " : " << getSrcMemRef().getType() << ", " << getDstMemRef().getType() 844 << ", " << getTagMemRef().getType(); 845 } 846 847 // Parse DmaStartOp. 848 // Ex: 849 // %dma_id = dma_start %src[%i, %j], %dst[%k, %l], %size, 850 // %tag[%index], %stride, %num_elt_per_stride : 851 // : memref<3076 x f32, 0>, 852 // memref<1024 x f32, 2>, 853 // memref<1 x i32> 854 // 855 ParseResult DmaStartOp::parse(OpAsmParser &parser, OperationState &result) { 856 OpAsmParser::OperandType srcMemRefInfo; 857 SmallVector<OpAsmParser::OperandType, 4> srcIndexInfos; 858 OpAsmParser::OperandType dstMemRefInfo; 859 SmallVector<OpAsmParser::OperandType, 4> dstIndexInfos; 860 OpAsmParser::OperandType numElementsInfo; 861 OpAsmParser::OperandType tagMemrefInfo; 862 SmallVector<OpAsmParser::OperandType, 4> tagIndexInfos; 863 SmallVector<OpAsmParser::OperandType, 2> strideInfo; 864 865 SmallVector<Type, 3> types; 866 auto indexType = parser.getBuilder().getIndexType(); 867 868 // Parse and resolve the following list of operands: 869 // *) source memref followed by its indices (in square brackets). 870 // *) destination memref followed by its indices (in square brackets). 871 // *) dma size in KiB. 872 if (parser.parseOperand(srcMemRefInfo) || 873 parser.parseOperandList(srcIndexInfos, OpAsmParser::Delimiter::Square) || 874 parser.parseComma() || parser.parseOperand(dstMemRefInfo) || 875 parser.parseOperandList(dstIndexInfos, OpAsmParser::Delimiter::Square) || 876 parser.parseComma() || parser.parseOperand(numElementsInfo) || 877 parser.parseComma() || parser.parseOperand(tagMemrefInfo) || 878 parser.parseOperandList(tagIndexInfos, OpAsmParser::Delimiter::Square)) 879 return failure(); 880 881 // Parse optional stride and elements per stride. 882 if (parser.parseTrailingOperandList(strideInfo)) 883 return failure(); 884 885 bool isStrided = strideInfo.size() == 2; 886 if (!strideInfo.empty() && !isStrided) { 887 return parser.emitError(parser.getNameLoc(), 888 "expected two stride related operands"); 889 } 890 891 if (parser.parseColonTypeList(types)) 892 return failure(); 893 if (types.size() != 3) 894 return parser.emitError(parser.getNameLoc(), "fewer/more types expected"); 895 896 if (parser.resolveOperand(srcMemRefInfo, types[0], result.operands) || 897 parser.resolveOperands(srcIndexInfos, indexType, result.operands) || 898 parser.resolveOperand(dstMemRefInfo, types[1], result.operands) || 899 parser.resolveOperands(dstIndexInfos, indexType, result.operands) || 900 // size should be an index. 901 parser.resolveOperand(numElementsInfo, indexType, result.operands) || 902 parser.resolveOperand(tagMemrefInfo, types[2], result.operands) || 903 // tag indices should be index. 904 parser.resolveOperands(tagIndexInfos, indexType, result.operands)) 905 return failure(); 906 907 if (isStrided) { 908 if (parser.resolveOperands(strideInfo, indexType, result.operands)) 909 return failure(); 910 } 911 912 return success(); 913 } 914 915 LogicalResult DmaStartOp::verify() { 916 unsigned numOperands = getNumOperands(); 917 918 // Mandatory non-variadic operands are: src memref, dst memref, tag memref and 919 // the number of elements. 920 if (numOperands < 4) 921 return emitOpError("expected at least 4 operands"); 922 923 // Check types of operands. The order of these calls is important: the later 924 // calls rely on some type properties to compute the operand position. 925 // 1. Source memref. 926 if (!getSrcMemRef().getType().isa<MemRefType>()) 927 return emitOpError("expected source to be of memref type"); 928 if (numOperands < getSrcMemRefRank() + 4) 929 return emitOpError() << "expected at least " << getSrcMemRefRank() + 4 930 << " operands"; 931 if (!getSrcIndices().empty() && 932 !llvm::all_of(getSrcIndices().getTypes(), 933 [](Type t) { return t.isIndex(); })) 934 return emitOpError("expected source indices to be of index type"); 935 936 // 2. Destination memref. 937 if (!getDstMemRef().getType().isa<MemRefType>()) 938 return emitOpError("expected destination to be of memref type"); 939 unsigned numExpectedOperands = getSrcMemRefRank() + getDstMemRefRank() + 4; 940 if (numOperands < numExpectedOperands) 941 return emitOpError() << "expected at least " << numExpectedOperands 942 << " operands"; 943 if (!getDstIndices().empty() && 944 !llvm::all_of(getDstIndices().getTypes(), 945 [](Type t) { return t.isIndex(); })) 946 return emitOpError("expected destination indices to be of index type"); 947 948 // 3. Number of elements. 949 if (!getNumElements().getType().isIndex()) 950 return emitOpError("expected num elements to be of index type"); 951 952 // 4. Tag memref. 953 if (!getTagMemRef().getType().isa<MemRefType>()) 954 return emitOpError("expected tag to be of memref type"); 955 numExpectedOperands += getTagMemRefRank(); 956 if (numOperands < numExpectedOperands) 957 return emitOpError() << "expected at least " << numExpectedOperands 958 << " operands"; 959 if (!getTagIndices().empty() && 960 !llvm::all_of(getTagIndices().getTypes(), 961 [](Type t) { return t.isIndex(); })) 962 return emitOpError("expected tag indices to be of index type"); 963 964 // Optional stride-related operands must be either both present or both 965 // absent. 966 if (numOperands != numExpectedOperands && 967 numOperands != numExpectedOperands + 2) 968 return emitOpError("incorrect number of operands"); 969 970 // 5. Strides. 971 if (isStrided()) { 972 if (!getStride().getType().isIndex() || 973 !getNumElementsPerStride().getType().isIndex()) 974 return emitOpError( 975 "expected stride and num elements per stride to be of type index"); 976 } 977 978 return success(); 979 } 980 981 LogicalResult DmaStartOp::fold(ArrayRef<Attribute> cstOperands, 982 SmallVectorImpl<OpFoldResult> &results) { 983 /// dma_start(memrefcast) -> dma_start 984 return foldMemRefCast(*this); 985 } 986 987 // --------------------------------------------------------------------------- 988 // DmaWaitOp 989 // --------------------------------------------------------------------------- 990 991 void DmaWaitOp::build(OpBuilder &builder, OperationState &result, 992 Value tagMemRef, ValueRange tagIndices, 993 Value numElements) { 994 result.addOperands(tagMemRef); 995 result.addOperands(tagIndices); 996 result.addOperands(numElements); 997 } 998 999 void DmaWaitOp::print(OpAsmPrinter &p) { 1000 p << getOperationName() << " " << getTagMemRef() << '[' << getTagIndices() 1001 << "], " << getNumElements(); 1002 p.printOptionalAttrDict((*this)->getAttrs()); 1003 p << " : " << getTagMemRef().getType(); 1004 } 1005 1006 // Parse DmaWaitOp. 1007 // Eg: 1008 // dma_wait %tag[%index], %num_elements : memref<1 x i32, (d0) -> (d0), 4> 1009 // 1010 ParseResult DmaWaitOp::parse(OpAsmParser &parser, OperationState &result) { 1011 OpAsmParser::OperandType tagMemrefInfo; 1012 SmallVector<OpAsmParser::OperandType, 2> tagIndexInfos; 1013 Type type; 1014 auto indexType = parser.getBuilder().getIndexType(); 1015 OpAsmParser::OperandType numElementsInfo; 1016 1017 // Parse tag memref, its indices, and dma size. 1018 if (parser.parseOperand(tagMemrefInfo) || 1019 parser.parseOperandList(tagIndexInfos, OpAsmParser::Delimiter::Square) || 1020 parser.parseComma() || parser.parseOperand(numElementsInfo) || 1021 parser.parseColonType(type) || 1022 parser.resolveOperand(tagMemrefInfo, type, result.operands) || 1023 parser.resolveOperands(tagIndexInfos, indexType, result.operands) || 1024 parser.resolveOperand(numElementsInfo, indexType, result.operands)) 1025 return failure(); 1026 1027 return success(); 1028 } 1029 1030 LogicalResult DmaWaitOp::fold(ArrayRef<Attribute> cstOperands, 1031 SmallVectorImpl<OpFoldResult> &results) { 1032 /// dma_wait(memrefcast) -> dma_wait 1033 return foldMemRefCast(*this); 1034 } 1035 1036 LogicalResult DmaWaitOp::verify() { 1037 // Mandatory non-variadic operands are tag and the number of elements. 1038 if (getNumOperands() < 2) 1039 return emitOpError() << "expected at least 2 operands"; 1040 1041 // Check types of operands. The order of these calls is important: the later 1042 // calls rely on some type properties to compute the operand position. 1043 if (!getTagMemRef().getType().isa<MemRefType>()) 1044 return emitOpError() << "expected tag to be of memref type"; 1045 1046 if (getNumOperands() != 2 + getTagMemRefRank()) 1047 return emitOpError() << "expected " << 2 + getTagMemRefRank() 1048 << " operands"; 1049 1050 if (!getTagIndices().empty() && 1051 !llvm::all_of(getTagIndices().getTypes(), 1052 [](Type t) { return t.isIndex(); })) 1053 return emitOpError() << "expected tag indices to be of index type"; 1054 1055 if (!getNumElements().getType().isIndex()) 1056 return emitOpError() 1057 << "expected the number of elements to be of index type"; 1058 1059 return success(); 1060 } 1061 1062 //===----------------------------------------------------------------------===// 1063 // GlobalOp 1064 //===----------------------------------------------------------------------===// 1065 1066 static void printGlobalMemrefOpTypeAndInitialValue(OpAsmPrinter &p, GlobalOp op, 1067 TypeAttr type, 1068 Attribute initialValue) { 1069 p << type; 1070 if (!op.isExternal()) { 1071 p << " = "; 1072 if (op.isUninitialized()) 1073 p << "uninitialized"; 1074 else 1075 p.printAttributeWithoutType(initialValue); 1076 } 1077 } 1078 1079 static ParseResult 1080 parseGlobalMemrefOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr, 1081 Attribute &initialValue) { 1082 Type type; 1083 if (parser.parseType(type)) 1084 return failure(); 1085 1086 auto memrefType = type.dyn_cast<MemRefType>(); 1087 if (!memrefType || !memrefType.hasStaticShape()) 1088 return parser.emitError(parser.getNameLoc()) 1089 << "type should be static shaped memref, but got " << type; 1090 typeAttr = TypeAttr::get(type); 1091 1092 if (parser.parseOptionalEqual()) 1093 return success(); 1094 1095 if (succeeded(parser.parseOptionalKeyword("uninitialized"))) { 1096 initialValue = UnitAttr::get(parser.getBuilder().getContext()); 1097 return success(); 1098 } 1099 1100 Type tensorType = getTensorTypeFromMemRefType(memrefType); 1101 if (parser.parseAttribute(initialValue, tensorType)) 1102 return failure(); 1103 if (!initialValue.isa<ElementsAttr>()) 1104 return parser.emitError(parser.getNameLoc()) 1105 << "initial value should be a unit or elements attribute"; 1106 return success(); 1107 } 1108 1109 static LogicalResult verify(GlobalOp op) { 1110 auto memrefType = op.type().dyn_cast<MemRefType>(); 1111 if (!memrefType || !memrefType.hasStaticShape()) 1112 return op.emitOpError("type should be static shaped memref, but got ") 1113 << op.type(); 1114 1115 // Verify that the initial value, if present, is either a unit attribute or 1116 // an elements attribute. 1117 if (op.initial_value().hasValue()) { 1118 Attribute initValue = op.initial_value().getValue(); 1119 if (!initValue.isa<UnitAttr>() && !initValue.isa<ElementsAttr>()) 1120 return op.emitOpError("initial value should be a unit or elements " 1121 "attribute, but got ") 1122 << initValue; 1123 1124 // Check that the type of the initial value is compatible with the type of 1125 // the global variable. 1126 if (initValue.isa<ElementsAttr>()) { 1127 Type initType = initValue.getType(); 1128 Type tensorType = getTensorTypeFromMemRefType(memrefType); 1129 if (initType != tensorType) 1130 return op.emitOpError("initial value expected to be of type ") 1131 << tensorType << ", but was of type " << initType; 1132 } 1133 } 1134 1135 // TODO: verify visibility for declarations. 1136 return success(); 1137 } 1138 1139 //===----------------------------------------------------------------------===// 1140 // GetGlobalOp 1141 //===----------------------------------------------------------------------===// 1142 1143 LogicalResult 1144 GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) { 1145 // Verify that the result type is same as the type of the referenced 1146 // memref.global op. 1147 auto global = 1148 symbolTable.lookupNearestSymbolFrom<GlobalOp>(*this, nameAttr()); 1149 if (!global) 1150 return emitOpError("'") 1151 << name() << "' does not reference a valid global memref"; 1152 1153 Type resultType = result().getType(); 1154 if (global.type() != resultType) 1155 return emitOpError("result type ") 1156 << resultType << " does not match type " << global.type() 1157 << " of the global memref @" << name(); 1158 return success(); 1159 } 1160 1161 //===----------------------------------------------------------------------===// 1162 // LoadOp 1163 //===----------------------------------------------------------------------===// 1164 1165 static LogicalResult verify(LoadOp op) { 1166 if (op.getNumOperands() != 1 + op.getMemRefType().getRank()) 1167 return op.emitOpError("incorrect number of indices for load"); 1168 return success(); 1169 } 1170 1171 OpFoldResult LoadOp::fold(ArrayRef<Attribute> cstOperands) { 1172 /// load(memrefcast) -> load 1173 if (succeeded(foldMemRefCast(*this))) 1174 return getResult(); 1175 return OpFoldResult(); 1176 } 1177 1178 namespace { 1179 /// Fold a load on a buffer_cast operation into an tensor.extract on the 1180 /// corresponding tensor. 1181 struct LoadOfBufferCast : public OpRewritePattern<LoadOp> { 1182 using OpRewritePattern<LoadOp>::OpRewritePattern; 1183 1184 LogicalResult matchAndRewrite(LoadOp load, 1185 PatternRewriter &rewriter) const override { 1186 auto buffercast = load.memref().getDefiningOp<BufferCastOp>(); 1187 if (!buffercast) 1188 return failure(); 1189 1190 rewriter.replaceOpWithNewOp<tensor::ExtractOp>(load, buffercast.tensor(), 1191 load.indices()); 1192 return success(); 1193 } 1194 }; 1195 } // end anonymous namespace. 1196 1197 void LoadOp::getCanonicalizationPatterns(RewritePatternSet &results, 1198 MLIRContext *context) { 1199 results.add<LoadOfBufferCast>(context); 1200 } 1201 1202 //===----------------------------------------------------------------------===// 1203 // PrefetchOp 1204 //===----------------------------------------------------------------------===// 1205 1206 static void print(OpAsmPrinter &p, PrefetchOp op) { 1207 p << PrefetchOp::getOperationName() << " " << op.memref() << '['; 1208 p.printOperands(op.indices()); 1209 p << ']' << ", " << (op.isWrite() ? "write" : "read"); 1210 p << ", locality<" << op.localityHint(); 1211 p << ">, " << (op.isDataCache() ? "data" : "instr"); 1212 p.printOptionalAttrDict( 1213 op->getAttrs(), 1214 /*elidedAttrs=*/{"localityHint", "isWrite", "isDataCache"}); 1215 p << " : " << op.getMemRefType(); 1216 } 1217 1218 static ParseResult parsePrefetchOp(OpAsmParser &parser, 1219 OperationState &result) { 1220 OpAsmParser::OperandType memrefInfo; 1221 SmallVector<OpAsmParser::OperandType, 4> indexInfo; 1222 IntegerAttr localityHint; 1223 MemRefType type; 1224 StringRef readOrWrite, cacheType; 1225 1226 auto indexTy = parser.getBuilder().getIndexType(); 1227 auto i32Type = parser.getBuilder().getIntegerType(32); 1228 if (parser.parseOperand(memrefInfo) || 1229 parser.parseOperandList(indexInfo, OpAsmParser::Delimiter::Square) || 1230 parser.parseComma() || parser.parseKeyword(&readOrWrite) || 1231 parser.parseComma() || parser.parseKeyword("locality") || 1232 parser.parseLess() || 1233 parser.parseAttribute(localityHint, i32Type, "localityHint", 1234 result.attributes) || 1235 parser.parseGreater() || parser.parseComma() || 1236 parser.parseKeyword(&cacheType) || parser.parseColonType(type) || 1237 parser.resolveOperand(memrefInfo, type, result.operands) || 1238 parser.resolveOperands(indexInfo, indexTy, result.operands)) 1239 return failure(); 1240 1241 if (!readOrWrite.equals("read") && !readOrWrite.equals("write")) 1242 return parser.emitError(parser.getNameLoc(), 1243 "rw specifier has to be 'read' or 'write'"); 1244 result.addAttribute( 1245 PrefetchOp::getIsWriteAttrName(), 1246 parser.getBuilder().getBoolAttr(readOrWrite.equals("write"))); 1247 1248 if (!cacheType.equals("data") && !cacheType.equals("instr")) 1249 return parser.emitError(parser.getNameLoc(), 1250 "cache type has to be 'data' or 'instr'"); 1251 1252 result.addAttribute( 1253 PrefetchOp::getIsDataCacheAttrName(), 1254 parser.getBuilder().getBoolAttr(cacheType.equals("data"))); 1255 1256 return success(); 1257 } 1258 1259 static LogicalResult verify(PrefetchOp op) { 1260 if (op.getNumOperands() != 1 + op.getMemRefType().getRank()) 1261 return op.emitOpError("too few indices"); 1262 1263 return success(); 1264 } 1265 1266 LogicalResult PrefetchOp::fold(ArrayRef<Attribute> cstOperands, 1267 SmallVectorImpl<OpFoldResult> &results) { 1268 // prefetch(memrefcast) -> prefetch 1269 return foldMemRefCast(*this); 1270 } 1271 1272 //===----------------------------------------------------------------------===// 1273 // ReinterpretCastOp 1274 //===----------------------------------------------------------------------===// 1275 1276 /// Build a ReinterpretCastOp with all dynamic entries: `staticOffsets`, 1277 /// `staticSizes` and `staticStrides` are automatically filled with 1278 /// source-memref-rank sentinel values that encode dynamic entries. 1279 void ReinterpretCastOp::build(OpBuilder &b, OperationState &result, 1280 MemRefType resultType, Value source, 1281 OpFoldResult offset, ArrayRef<OpFoldResult> sizes, 1282 ArrayRef<OpFoldResult> strides, 1283 ArrayRef<NamedAttribute> attrs) { 1284 SmallVector<int64_t> staticOffsets, staticSizes, staticStrides; 1285 SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides; 1286 dispatchIndexOpFoldResults(offset, dynamicOffsets, staticOffsets, 1287 ShapedType::kDynamicStrideOrOffset); 1288 dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes, 1289 ShapedType::kDynamicSize); 1290 dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides, 1291 ShapedType::kDynamicStrideOrOffset); 1292 build(b, result, resultType, source, dynamicOffsets, dynamicSizes, 1293 dynamicStrides, b.getI64ArrayAttr(staticOffsets), 1294 b.getI64ArrayAttr(staticSizes), b.getI64ArrayAttr(staticStrides)); 1295 result.addAttributes(attrs); 1296 } 1297 1298 void ReinterpretCastOp::build(OpBuilder &b, OperationState &result, 1299 MemRefType resultType, Value source, 1300 int64_t offset, ArrayRef<int64_t> sizes, 1301 ArrayRef<int64_t> strides, 1302 ArrayRef<NamedAttribute> attrs) { 1303 SmallVector<OpFoldResult> sizeValues = 1304 llvm::to_vector<4>(llvm::map_range(sizes, [&](int64_t v) -> OpFoldResult { 1305 return b.getI64IntegerAttr(v); 1306 })); 1307 SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>( 1308 llvm::map_range(strides, [&](int64_t v) -> OpFoldResult { 1309 return b.getI64IntegerAttr(v); 1310 })); 1311 build(b, result, resultType, source, b.getI64IntegerAttr(offset), sizeValues, 1312 strideValues, attrs); 1313 } 1314 1315 void ReinterpretCastOp::build(OpBuilder &b, OperationState &result, 1316 MemRefType resultType, Value source, Value offset, 1317 ValueRange sizes, ValueRange strides, 1318 ArrayRef<NamedAttribute> attrs) { 1319 SmallVector<OpFoldResult> sizeValues = llvm::to_vector<4>( 1320 llvm::map_range(sizes, [](Value v) -> OpFoldResult { return v; })); 1321 SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>( 1322 llvm::map_range(strides, [](Value v) -> OpFoldResult { return v; })); 1323 build(b, result, resultType, source, offset, sizeValues, strideValues, attrs); 1324 } 1325 1326 // TODO: ponder whether we want to allow missing trailing sizes/strides that are 1327 // completed automatically, like we have for subview and subtensor. 1328 static LogicalResult verify(ReinterpretCastOp op) { 1329 // The source and result memrefs should be in the same memory space. 1330 auto srcType = op.source().getType().cast<BaseMemRefType>(); 1331 auto resultType = op.getType().cast<MemRefType>(); 1332 if (srcType.getMemorySpace() != resultType.getMemorySpace()) 1333 return op.emitError("different memory spaces specified for source type ") 1334 << srcType << " and result memref type " << resultType; 1335 if (srcType.getElementType() != resultType.getElementType()) 1336 return op.emitError("different element types specified for source type ") 1337 << srcType << " and result memref type " << resultType; 1338 1339 // Match sizes in result memref type and in static_sizes attribute. 1340 for (auto &en : 1341 llvm::enumerate(llvm::zip(resultType.getShape(), 1342 extractFromI64ArrayAttr(op.static_sizes())))) { 1343 int64_t resultSize = std::get<0>(en.value()); 1344 int64_t expectedSize = std::get<1>(en.value()); 1345 if (resultSize != expectedSize) 1346 return op.emitError("expected result type with size = ") 1347 << expectedSize << " instead of " << resultSize 1348 << " in dim = " << en.index(); 1349 } 1350 1351 // Match offset and strides in static_offset and static_strides attributes if 1352 // result memref type has an affine map specified. 1353 if (!resultType.getAffineMaps().empty()) { 1354 int64_t resultOffset; 1355 SmallVector<int64_t, 4> resultStrides; 1356 if (failed(getStridesAndOffset(resultType, resultStrides, resultOffset))) 1357 return failure(); 1358 1359 // Match offset in result memref type and in static_offsets attribute. 1360 int64_t expectedOffset = 1361 extractFromI64ArrayAttr(op.static_offsets()).front(); 1362 if (resultOffset != expectedOffset) 1363 return op.emitError("expected result type with offset = ") 1364 << resultOffset << " instead of " << expectedOffset; 1365 1366 // Match strides in result memref type and in static_strides attribute. 1367 for (auto &en : llvm::enumerate(llvm::zip( 1368 resultStrides, extractFromI64ArrayAttr(op.static_strides())))) { 1369 int64_t resultStride = std::get<0>(en.value()); 1370 int64_t expectedStride = std::get<1>(en.value()); 1371 if (resultStride != expectedStride) 1372 return op.emitError("expected result type with stride = ") 1373 << expectedStride << " instead of " << resultStride 1374 << " in dim = " << en.index(); 1375 } 1376 } 1377 return success(); 1378 } 1379 1380 //===----------------------------------------------------------------------===// 1381 // ReshapeOp 1382 //===----------------------------------------------------------------------===// 1383 1384 static LogicalResult verify(ReshapeOp op) { 1385 Type operandType = op.source().getType(); 1386 Type resultType = op.result().getType(); 1387 1388 Type operandElementType = operandType.cast<ShapedType>().getElementType(); 1389 Type resultElementType = resultType.cast<ShapedType>().getElementType(); 1390 if (operandElementType != resultElementType) 1391 return op.emitOpError("element types of source and destination memref " 1392 "types should be the same"); 1393 1394 if (auto operandMemRefType = operandType.dyn_cast<MemRefType>()) 1395 if (!operandMemRefType.getAffineMaps().empty()) 1396 return op.emitOpError( 1397 "source memref type should have identity affine map"); 1398 1399 int64_t shapeSize = op.shape().getType().cast<MemRefType>().getDimSize(0); 1400 auto resultMemRefType = resultType.dyn_cast<MemRefType>(); 1401 if (resultMemRefType) { 1402 if (!resultMemRefType.getAffineMaps().empty()) 1403 return op.emitOpError( 1404 "result memref type should have identity affine map"); 1405 if (shapeSize == ShapedType::kDynamicSize) 1406 return op.emitOpError("cannot use shape operand with dynamic length to " 1407 "reshape to statically-ranked memref type"); 1408 if (shapeSize != resultMemRefType.getRank()) 1409 return op.emitOpError( 1410 "length of shape operand differs from the result's memref rank"); 1411 } 1412 return success(); 1413 } 1414 1415 //===----------------------------------------------------------------------===// 1416 // StoreOp 1417 //===----------------------------------------------------------------------===// 1418 1419 static LogicalResult verify(StoreOp op) { 1420 if (op.getNumOperands() != 2 + op.getMemRefType().getRank()) 1421 return op.emitOpError("store index operand count not equal to memref rank"); 1422 1423 return success(); 1424 } 1425 1426 LogicalResult StoreOp::fold(ArrayRef<Attribute> cstOperands, 1427 SmallVectorImpl<OpFoldResult> &results) { 1428 /// store(memrefcast) -> store 1429 return foldMemRefCast(*this, getValueToStore()); 1430 } 1431 1432 //===----------------------------------------------------------------------===// 1433 // SubViewOp 1434 //===----------------------------------------------------------------------===// 1435 1436 namespace { 1437 /// Helpers to write more idiomatic operations. 1438 namespace saturated_arith { 1439 struct Wrapper { 1440 explicit Wrapper(int64_t v) : v(v) {} 1441 operator int64_t() { return v; } 1442 int64_t v; 1443 }; 1444 Wrapper operator+(Wrapper a, int64_t b) { 1445 if (ShapedType::isDynamicStrideOrOffset(a) || 1446 ShapedType::isDynamicStrideOrOffset(b)) 1447 return Wrapper(ShapedType::kDynamicStrideOrOffset); 1448 return Wrapper(a.v + b); 1449 } 1450 Wrapper operator*(Wrapper a, int64_t b) { 1451 if (ShapedType::isDynamicStrideOrOffset(a) || 1452 ShapedType::isDynamicStrideOrOffset(b)) 1453 return Wrapper(ShapedType::kDynamicStrideOrOffset); 1454 return Wrapper(a.v * b); 1455 } 1456 } // end namespace saturated_arith 1457 } // end namespace 1458 1459 /// A subview result type can be fully inferred from the source type and the 1460 /// static representation of offsets, sizes and strides. Special sentinels 1461 /// encode the dynamic case. 1462 Type SubViewOp::inferResultType(MemRefType sourceMemRefType, 1463 ArrayRef<int64_t> leadingStaticOffsets, 1464 ArrayRef<int64_t> leadingStaticSizes, 1465 ArrayRef<int64_t> leadingStaticStrides) { 1466 // A subview may specify only a leading subset of offset/sizes/strides in 1467 // which case we complete with offset=0, sizes from memref type and strides=1. 1468 unsigned rank = sourceMemRefType.getRank(); 1469 assert(leadingStaticOffsets.size() <= rank && 1470 "unexpected leadingStaticOffsets overflow"); 1471 assert(leadingStaticSizes.size() <= rank && 1472 "unexpected leadingStaticSizes overflow"); 1473 assert(leadingStaticStrides.size() <= rank && 1474 "unexpected leadingStaticStrides overflow"); 1475 auto staticOffsets = llvm::to_vector<4>(leadingStaticOffsets); 1476 auto staticSizes = llvm::to_vector<4>(leadingStaticSizes); 1477 auto staticStrides = llvm::to_vector<4>(leadingStaticStrides); 1478 unsigned numTrailingOffsets = rank - staticOffsets.size(); 1479 unsigned numTrailingSizes = rank - staticSizes.size(); 1480 unsigned numTrailingStrides = rank - staticStrides.size(); 1481 staticOffsets.append(numTrailingOffsets, 0); 1482 llvm::append_range(staticSizes, 1483 sourceMemRefType.getShape().take_back(numTrailingSizes)); 1484 staticStrides.append(numTrailingStrides, 1); 1485 1486 // Extract source offset and strides. 1487 int64_t sourceOffset; 1488 SmallVector<int64_t, 4> sourceStrides; 1489 auto res = getStridesAndOffset(sourceMemRefType, sourceStrides, sourceOffset); 1490 assert(succeeded(res) && "SubViewOp expected strided memref type"); 1491 (void)res; 1492 1493 // Compute target offset whose value is: 1494 // `sourceOffset + sum_i(staticOffset_i * sourceStrides_i)`. 1495 int64_t targetOffset = sourceOffset; 1496 for (auto it : llvm::zip(staticOffsets, sourceStrides)) { 1497 auto staticOffset = std::get<0>(it), targetStride = std::get<1>(it); 1498 using namespace saturated_arith; 1499 targetOffset = Wrapper(targetOffset) + Wrapper(staticOffset) * targetStride; 1500 } 1501 1502 // Compute target stride whose value is: 1503 // `sourceStrides_i * staticStrides_i`. 1504 SmallVector<int64_t, 4> targetStrides; 1505 targetStrides.reserve(staticOffsets.size()); 1506 for (auto it : llvm::zip(sourceStrides, staticStrides)) { 1507 auto sourceStride = std::get<0>(it), staticStride = std::get<1>(it); 1508 using namespace saturated_arith; 1509 targetStrides.push_back(Wrapper(sourceStride) * staticStride); 1510 } 1511 1512 // The type is now known. 1513 return MemRefType::get( 1514 staticSizes, sourceMemRefType.getElementType(), 1515 makeStridedLinearLayoutMap(targetStrides, targetOffset, 1516 sourceMemRefType.getContext()), 1517 sourceMemRefType.getMemorySpace()); 1518 } 1519 1520 Type SubViewOp::inferResultType(MemRefType sourceMemRefType, 1521 ArrayRef<OpFoldResult> leadingStaticOffsets, 1522 ArrayRef<OpFoldResult> leadingStaticSizes, 1523 ArrayRef<OpFoldResult> leadingStaticStrides) { 1524 SmallVector<int64_t> staticOffsets, staticSizes, staticStrides; 1525 SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides; 1526 dispatchIndexOpFoldResults(leadingStaticOffsets, dynamicOffsets, 1527 staticOffsets, ShapedType::kDynamicStrideOrOffset); 1528 dispatchIndexOpFoldResults(leadingStaticSizes, dynamicSizes, staticSizes, 1529 ShapedType::kDynamicSize); 1530 dispatchIndexOpFoldResults(leadingStaticStrides, dynamicStrides, 1531 staticStrides, ShapedType::kDynamicStrideOrOffset); 1532 return SubViewOp::inferResultType(sourceMemRefType, staticOffsets, 1533 staticSizes, staticStrides) 1534 .cast<MemRefType>(); 1535 } 1536 1537 Type SubViewOp::inferRankReducedResultType( 1538 unsigned resultRank, MemRefType sourceRankedTensorType, 1539 ArrayRef<int64_t> leadingStaticOffsets, 1540 ArrayRef<int64_t> leadingStaticSizes, 1541 ArrayRef<int64_t> leadingStaticStrides) { 1542 auto inferredType = 1543 inferResultType(sourceRankedTensorType, leadingStaticOffsets, 1544 leadingStaticSizes, leadingStaticStrides) 1545 .cast<MemRefType>(); 1546 assert(inferredType.getRank() >= resultRank && "expected "); 1547 int rankDiff = inferredType.getRank() - resultRank; 1548 if (rankDiff > 0) { 1549 auto shape = inferredType.getShape(); 1550 llvm::SmallDenseSet<unsigned> dimsToProject; 1551 mlir::getPositionsOfShapeOne(rankDiff, shape, dimsToProject); 1552 SmallVector<int64_t> projectedShape; 1553 for (unsigned pos = 0, e = shape.size(); pos < e; ++pos) 1554 if (!dimsToProject.contains(pos)) 1555 projectedShape.push_back(shape[pos]); 1556 1557 AffineMap map; 1558 auto maps = inferredType.getAffineMaps(); 1559 if (!maps.empty() && maps.front()) 1560 map = getProjectedMap(maps.front(), dimsToProject); 1561 inferredType = 1562 MemRefType::get(projectedShape, inferredType.getElementType(), map, 1563 inferredType.getMemorySpace()); 1564 } 1565 return inferredType; 1566 } 1567 1568 Type SubViewOp::inferRankReducedResultType( 1569 unsigned resultRank, MemRefType sourceRankedTensorType, 1570 ArrayRef<OpFoldResult> leadingStaticOffsets, 1571 ArrayRef<OpFoldResult> leadingStaticSizes, 1572 ArrayRef<OpFoldResult> leadingStaticStrides) { 1573 SmallVector<int64_t> staticOffsets, staticSizes, staticStrides; 1574 SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides; 1575 dispatchIndexOpFoldResults(leadingStaticOffsets, dynamicOffsets, 1576 staticOffsets, ShapedType::kDynamicStrideOrOffset); 1577 dispatchIndexOpFoldResults(leadingStaticSizes, dynamicSizes, staticSizes, 1578 ShapedType::kDynamicSize); 1579 dispatchIndexOpFoldResults(leadingStaticStrides, dynamicStrides, 1580 staticStrides, ShapedType::kDynamicStrideOrOffset); 1581 return SubViewOp::inferRankReducedResultType( 1582 resultRank, sourceRankedTensorType, staticOffsets, staticSizes, 1583 staticStrides); 1584 } 1585 // Build a SubViewOp with mixed static and dynamic entries and custom result 1586 // type. If the type passed is nullptr, it is inferred. 1587 void SubViewOp::build(OpBuilder &b, OperationState &result, 1588 MemRefType resultType, Value source, 1589 ArrayRef<OpFoldResult> offsets, 1590 ArrayRef<OpFoldResult> sizes, 1591 ArrayRef<OpFoldResult> strides, 1592 ArrayRef<NamedAttribute> attrs) { 1593 SmallVector<int64_t> staticOffsets, staticSizes, staticStrides; 1594 SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides; 1595 dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets, 1596 ShapedType::kDynamicStrideOrOffset); 1597 dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes, 1598 ShapedType::kDynamicSize); 1599 dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides, 1600 ShapedType::kDynamicStrideOrOffset); 1601 auto sourceMemRefType = source.getType().cast<MemRefType>(); 1602 // Structuring implementation this way avoids duplication between builders. 1603 if (!resultType) { 1604 resultType = SubViewOp::inferResultType(sourceMemRefType, staticOffsets, 1605 staticSizes, staticStrides) 1606 .cast<MemRefType>(); 1607 } 1608 build(b, result, resultType, source, dynamicOffsets, dynamicSizes, 1609 dynamicStrides, b.getI64ArrayAttr(staticOffsets), 1610 b.getI64ArrayAttr(staticSizes), b.getI64ArrayAttr(staticStrides)); 1611 result.addAttributes(attrs); 1612 } 1613 1614 // Build a SubViewOp with mixed static and dynamic entries and inferred result 1615 // type. 1616 void SubViewOp::build(OpBuilder &b, OperationState &result, Value source, 1617 ArrayRef<OpFoldResult> offsets, 1618 ArrayRef<OpFoldResult> sizes, 1619 ArrayRef<OpFoldResult> strides, 1620 ArrayRef<NamedAttribute> attrs) { 1621 build(b, result, MemRefType(), source, offsets, sizes, strides, attrs); 1622 } 1623 1624 // Build a SubViewOp with static entries and inferred result type. 1625 void SubViewOp::build(OpBuilder &b, OperationState &result, Value source, 1626 ArrayRef<int64_t> offsets, ArrayRef<int64_t> sizes, 1627 ArrayRef<int64_t> strides, 1628 ArrayRef<NamedAttribute> attrs) { 1629 SmallVector<OpFoldResult> offsetValues = llvm::to_vector<4>( 1630 llvm::map_range(offsets, [&](int64_t v) -> OpFoldResult { 1631 return b.getI64IntegerAttr(v); 1632 })); 1633 SmallVector<OpFoldResult> sizeValues = 1634 llvm::to_vector<4>(llvm::map_range(sizes, [&](int64_t v) -> OpFoldResult { 1635 return b.getI64IntegerAttr(v); 1636 })); 1637 SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>( 1638 llvm::map_range(strides, [&](int64_t v) -> OpFoldResult { 1639 return b.getI64IntegerAttr(v); 1640 })); 1641 build(b, result, source, offsetValues, sizeValues, strideValues, attrs); 1642 } 1643 1644 // Build a SubViewOp with dynamic entries and custom result type. If the 1645 // type passed is nullptr, it is inferred. 1646 void SubViewOp::build(OpBuilder &b, OperationState &result, 1647 MemRefType resultType, Value source, 1648 ArrayRef<int64_t> offsets, ArrayRef<int64_t> sizes, 1649 ArrayRef<int64_t> strides, 1650 ArrayRef<NamedAttribute> attrs) { 1651 SmallVector<OpFoldResult> offsetValues = llvm::to_vector<4>( 1652 llvm::map_range(offsets, [&](int64_t v) -> OpFoldResult { 1653 return b.getI64IntegerAttr(v); 1654 })); 1655 SmallVector<OpFoldResult> sizeValues = 1656 llvm::to_vector<4>(llvm::map_range(sizes, [&](int64_t v) -> OpFoldResult { 1657 return b.getI64IntegerAttr(v); 1658 })); 1659 SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>( 1660 llvm::map_range(strides, [&](int64_t v) -> OpFoldResult { 1661 return b.getI64IntegerAttr(v); 1662 })); 1663 build(b, result, resultType, source, offsetValues, sizeValues, strideValues, 1664 attrs); 1665 } 1666 1667 // Build a SubViewOp with dynamic entries and custom result type. If the type 1668 // passed is nullptr, it is inferred. 1669 void SubViewOp::build(OpBuilder &b, OperationState &result, 1670 MemRefType resultType, Value source, ValueRange offsets, 1671 ValueRange sizes, ValueRange strides, 1672 ArrayRef<NamedAttribute> attrs) { 1673 SmallVector<OpFoldResult> offsetValues = llvm::to_vector<4>( 1674 llvm::map_range(offsets, [](Value v) -> OpFoldResult { return v; })); 1675 SmallVector<OpFoldResult> sizeValues = llvm::to_vector<4>( 1676 llvm::map_range(sizes, [](Value v) -> OpFoldResult { return v; })); 1677 SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>( 1678 llvm::map_range(strides, [](Value v) -> OpFoldResult { return v; })); 1679 build(b, result, resultType, source, offsetValues, sizeValues, strideValues); 1680 } 1681 1682 // Build a SubViewOp with dynamic entries and inferred result type. 1683 void SubViewOp::build(OpBuilder &b, OperationState &result, Value source, 1684 ValueRange offsets, ValueRange sizes, ValueRange strides, 1685 ArrayRef<NamedAttribute> attrs) { 1686 build(b, result, MemRefType(), source, offsets, sizes, strides, attrs); 1687 } 1688 1689 /// For ViewLikeOpInterface. 1690 Value SubViewOp::getViewSource() { return source(); } 1691 1692 enum SubViewVerificationResult { 1693 Success, 1694 RankTooLarge, 1695 SizeMismatch, 1696 ElemTypeMismatch, 1697 MemSpaceMismatch, 1698 AffineMapMismatch 1699 }; 1700 1701 /// Checks if `original` Type type can be rank reduced to `reduced` type. 1702 /// This function is slight variant of `is subsequence` algorithm where 1703 /// not matching dimension must be 1. 1704 static SubViewVerificationResult 1705 isRankReducedType(Type originalType, Type candidateReducedType, 1706 std::string *errMsg = nullptr) { 1707 if (originalType == candidateReducedType) 1708 return SubViewVerificationResult::Success; 1709 if (!originalType.isa<MemRefType>()) 1710 return SubViewVerificationResult::Success; 1711 if (originalType.isa<MemRefType>() && !candidateReducedType.isa<MemRefType>()) 1712 return SubViewVerificationResult::Success; 1713 1714 ShapedType originalShapedType = originalType.cast<ShapedType>(); 1715 ShapedType candidateReducedShapedType = 1716 candidateReducedType.cast<ShapedType>(); 1717 1718 // Rank and size logic is valid for all ShapedTypes. 1719 ArrayRef<int64_t> originalShape = originalShapedType.getShape(); 1720 ArrayRef<int64_t> candidateReducedShape = 1721 candidateReducedShapedType.getShape(); 1722 unsigned originalRank = originalShape.size(), 1723 candidateReducedRank = candidateReducedShape.size(); 1724 if (candidateReducedRank > originalRank) 1725 return SubViewVerificationResult::RankTooLarge; 1726 1727 auto optionalUnusedDimsMask = 1728 computeRankReductionMask(originalShape, candidateReducedShape); 1729 1730 // Sizes cannot be matched in case empty vector is returned. 1731 if (!optionalUnusedDimsMask.hasValue()) 1732 return SubViewVerificationResult::SizeMismatch; 1733 1734 if (originalShapedType.getElementType() != 1735 candidateReducedShapedType.getElementType()) 1736 return SubViewVerificationResult::ElemTypeMismatch; 1737 1738 // Strided layout logic is relevant for MemRefType only. 1739 MemRefType original = originalType.cast<MemRefType>(); 1740 MemRefType candidateReduced = candidateReducedType.cast<MemRefType>(); 1741 if (original.getMemorySpace() != candidateReduced.getMemorySpace()) 1742 return SubViewVerificationResult::MemSpaceMismatch; 1743 1744 llvm::SmallDenseSet<unsigned> unusedDims = optionalUnusedDimsMask.getValue(); 1745 auto inferredType = 1746 getProjectedMap(getStridedLinearLayoutMap(original), unusedDims); 1747 AffineMap candidateLayout; 1748 if (candidateReduced.getAffineMaps().empty()) 1749 candidateLayout = getStridedLinearLayoutMap(candidateReduced); 1750 else 1751 candidateLayout = candidateReduced.getAffineMaps().front(); 1752 assert(inferredType.getNumResults() == 1 && 1753 candidateLayout.getNumResults() == 1); 1754 if (inferredType.getNumSymbols() != candidateLayout.getNumSymbols() || 1755 inferredType.getNumDims() != candidateLayout.getNumDims()) { 1756 if (errMsg) { 1757 llvm::raw_string_ostream os(*errMsg); 1758 os << "inferred type: " << inferredType; 1759 } 1760 return SubViewVerificationResult::AffineMapMismatch; 1761 } 1762 // Check that the difference of the affine maps simplifies to 0. 1763 AffineExpr diffExpr = 1764 inferredType.getResult(0) - candidateLayout.getResult(0); 1765 diffExpr = simplifyAffineExpr(diffExpr, inferredType.getNumDims(), 1766 inferredType.getNumSymbols()); 1767 auto cst = diffExpr.dyn_cast<AffineConstantExpr>(); 1768 if (!(cst && cst.getValue() == 0)) { 1769 if (errMsg) { 1770 llvm::raw_string_ostream os(*errMsg); 1771 os << "inferred type: " << inferredType; 1772 } 1773 return SubViewVerificationResult::AffineMapMismatch; 1774 } 1775 return SubViewVerificationResult::Success; 1776 } 1777 1778 template <typename OpTy> 1779 static LogicalResult produceSubViewErrorMsg(SubViewVerificationResult result, 1780 OpTy op, Type expectedType, 1781 StringRef errMsg = "") { 1782 auto memrefType = expectedType.cast<ShapedType>(); 1783 switch (result) { 1784 case SubViewVerificationResult::Success: 1785 return success(); 1786 case SubViewVerificationResult::RankTooLarge: 1787 return op.emitError("expected result rank to be smaller or equal to ") 1788 << "the source rank. " << errMsg; 1789 case SubViewVerificationResult::SizeMismatch: 1790 return op.emitError("expected result type to be ") 1791 << expectedType 1792 << " or a rank-reduced version. (mismatch of result sizes) " 1793 << errMsg; 1794 case SubViewVerificationResult::ElemTypeMismatch: 1795 return op.emitError("expected result element type to be ") 1796 << memrefType.getElementType() << errMsg; 1797 case SubViewVerificationResult::MemSpaceMismatch: 1798 return op.emitError("expected result and source memory spaces to match.") 1799 << errMsg; 1800 case SubViewVerificationResult::AffineMapMismatch: 1801 return op.emitError("expected result type to be ") 1802 << expectedType 1803 << " or a rank-reduced version. (mismatch of result affine map) " 1804 << errMsg; 1805 } 1806 llvm_unreachable("unexpected subview verification result"); 1807 } 1808 1809 /// Verifier for SubViewOp. 1810 static LogicalResult verify(SubViewOp op) { 1811 MemRefType baseType = op.getSourceType(); 1812 MemRefType subViewType = op.getType(); 1813 1814 // The base memref and the view memref should be in the same memory space. 1815 if (baseType.getMemorySpace() != subViewType.getMemorySpace()) 1816 return op.emitError("different memory spaces specified for base memref " 1817 "type ") 1818 << baseType << " and subview memref type " << subViewType; 1819 1820 // Verify that the base memref type has a strided layout map. 1821 if (!isStrided(baseType)) 1822 return op.emitError("base type ") << baseType << " is not strided"; 1823 1824 // Verify result type against inferred type. 1825 auto expectedType = SubViewOp::inferResultType( 1826 baseType, extractFromI64ArrayAttr(op.static_offsets()), 1827 extractFromI64ArrayAttr(op.static_sizes()), 1828 extractFromI64ArrayAttr(op.static_strides())); 1829 1830 std::string errMsg; 1831 auto result = isRankReducedType(expectedType, subViewType, &errMsg); 1832 return produceSubViewErrorMsg(result, op, expectedType, errMsg); 1833 } 1834 1835 raw_ostream &mlir::operator<<(raw_ostream &os, Range &range) { 1836 return os << "range " << range.offset << ":" << range.size << ":" 1837 << range.stride; 1838 } 1839 1840 /// Return the list of Range (i.e. offset, size, stride). Each Range 1841 /// entry contains either the dynamic value or a ConstantIndexOp constructed 1842 /// with `b` at location `loc`. 1843 SmallVector<Range, 8> mlir::getOrCreateRanges(OffsetSizeAndStrideOpInterface op, 1844 OpBuilder &b, Location loc) { 1845 std::array<unsigned, 3> ranks = op.getArrayAttrMaxRanks(); 1846 assert(ranks[0] == ranks[1] && "expected offset and sizes of equal ranks"); 1847 assert(ranks[1] == ranks[2] && "expected sizes and strides of equal ranks"); 1848 SmallVector<Range, 8> res; 1849 unsigned rank = ranks[0]; 1850 res.reserve(rank); 1851 for (unsigned idx = 0; idx < rank; ++idx) { 1852 Value offset = 1853 op.isDynamicOffset(idx) 1854 ? op.getDynamicOffset(idx) 1855 : b.create<ConstantIndexOp>(loc, op.getStaticOffset(idx)); 1856 Value size = op.isDynamicSize(idx) 1857 ? op.getDynamicSize(idx) 1858 : b.create<ConstantIndexOp>(loc, op.getStaticSize(idx)); 1859 Value stride = 1860 op.isDynamicStride(idx) 1861 ? op.getDynamicStride(idx) 1862 : b.create<ConstantIndexOp>(loc, op.getStaticStride(idx)); 1863 res.emplace_back(Range{offset, size, stride}); 1864 } 1865 return res; 1866 } 1867 1868 /// Infer the canonical type of the result of a subview operation. Returns a 1869 /// type with rank `resultRank` that is either the rank of the rank-reduced 1870 /// type, or the non-rank-reduced type. 1871 static MemRefType 1872 getCanonicalSubViewResultType(unsigned resultRank, MemRefType sourceType, 1873 ArrayRef<OpFoldResult> mixedOffsets, 1874 ArrayRef<OpFoldResult> mixedSizes, 1875 ArrayRef<OpFoldResult> mixedStrides) { 1876 auto resultType = 1877 SubViewOp::inferRankReducedResultType( 1878 resultRank, sourceType, mixedOffsets, mixedSizes, mixedStrides) 1879 .cast<MemRefType>(); 1880 if (resultType.getRank() != resultRank) { 1881 resultType = SubViewOp::inferResultType(sourceType, mixedOffsets, 1882 mixedSizes, mixedStrides) 1883 .cast<MemRefType>(); 1884 } 1885 return resultType; 1886 } 1887 1888 namespace { 1889 /// Pattern to rewrite a subview op with MemRefCast arguments. 1890 /// This essentially pushes memref.cast past its consuming subview when 1891 /// `canFoldIntoConsumerOp` is true. 1892 /// 1893 /// Example: 1894 /// ``` 1895 /// %0 = memref.cast %V : memref<16x16xf32> to memref<?x?xf32> 1896 /// %1 = memref.subview %0[0, 0][3, 4][1, 1] : 1897 /// memref<?x?xf32> to memref<3x4xf32, offset:?, strides:[?, 1]> 1898 /// ``` 1899 /// is rewritten into: 1900 /// ``` 1901 /// %0 = memref.subview %V: memref<16x16xf32> to memref<3x4xf32, #[[map0]]> 1902 /// %1 = memref.cast %0: memref<3x4xf32, offset:0, strides:[16, 1]> to 1903 /// memref<3x4xf32, offset:?, strides:[?, 1]> 1904 /// ``` 1905 class SubViewOpMemRefCastFolder final : public OpRewritePattern<SubViewOp> { 1906 public: 1907 using OpRewritePattern<SubViewOp>::OpRewritePattern; 1908 1909 LogicalResult matchAndRewrite(SubViewOp subViewOp, 1910 PatternRewriter &rewriter) const override { 1911 // Any constant operand, just return to let SubViewOpConstantFolder kick in. 1912 if (llvm::any_of(subViewOp.getOperands(), [](Value operand) { 1913 return matchPattern(operand, matchConstantIndex()); 1914 })) 1915 return failure(); 1916 1917 auto castOp = subViewOp.source().getDefiningOp<CastOp>(); 1918 if (!castOp) 1919 return failure(); 1920 1921 if (!CastOp::canFoldIntoConsumerOp(castOp)) 1922 return failure(); 1923 1924 /// Deduce the resultType of the SubViewOp using `inferSubViewResultType` on 1925 /// the cast source operand type and the SubViewOp static information. This 1926 /// is the resulting type if the MemRefCastOp were folded. 1927 auto resultType = getCanonicalSubViewResultType( 1928 subViewOp.getType().getRank(), 1929 castOp.source().getType().cast<MemRefType>(), 1930 subViewOp.getMixedOffsets(), subViewOp.getMixedSizes(), 1931 subViewOp.getMixedStrides()); 1932 Value newSubView = rewriter.create<SubViewOp>( 1933 subViewOp.getLoc(), resultType, castOp.source(), subViewOp.offsets(), 1934 subViewOp.sizes(), subViewOp.strides(), subViewOp.static_offsets(), 1935 subViewOp.static_sizes(), subViewOp.static_strides()); 1936 rewriter.replaceOpWithNewOp<CastOp>(subViewOp, subViewOp.getType(), 1937 newSubView); 1938 return success(); 1939 } 1940 }; 1941 } // namespace 1942 1943 /// Return the canonical type of the result of a subview. 1944 struct SubViewReturnTypeCanonicalizer { 1945 MemRefType operator()(SubViewOp op, ArrayRef<OpFoldResult> mixedOffsets, 1946 ArrayRef<OpFoldResult> mixedSizes, 1947 ArrayRef<OpFoldResult> mixedStrides) { 1948 return getCanonicalSubViewResultType(op.getType().getRank(), 1949 op.getSourceType(), mixedOffsets, 1950 mixedSizes, mixedStrides); 1951 } 1952 }; 1953 1954 /// A canonicalizer wrapper to replace SubViewOps. 1955 struct SubViewCanonicalizer { 1956 void operator()(PatternRewriter &rewriter, SubViewOp op, SubViewOp newOp) { 1957 rewriter.replaceOpWithNewOp<CastOp>(op, newOp, op.getType()); 1958 } 1959 }; 1960 1961 void SubViewOp::getCanonicalizationPatterns(RewritePatternSet &results, 1962 MLIRContext *context) { 1963 results 1964 .add<OpWithOffsetSizesAndStridesConstantArgumentFolder< 1965 SubViewOp, SubViewReturnTypeCanonicalizer, SubViewCanonicalizer>, 1966 SubViewOpMemRefCastFolder>(context); 1967 } 1968 1969 OpFoldResult SubViewOp::fold(ArrayRef<Attribute> operands) { 1970 auto resultShapedType = getResult().getType().cast<ShapedType>(); 1971 auto sourceShapedType = source().getType().cast<ShapedType>(); 1972 1973 if (resultShapedType.hasStaticShape() && 1974 resultShapedType == sourceShapedType) { 1975 return getViewSource(); 1976 } 1977 1978 return {}; 1979 } 1980 1981 //===----------------------------------------------------------------------===// 1982 // TensorLoadOp 1983 //===----------------------------------------------------------------------===// 1984 1985 OpFoldResult TensorLoadOp::fold(ArrayRef<Attribute>) { 1986 if (auto bufferCast = memref().getDefiningOp<BufferCastOp>()) 1987 // Approximate alias analysis by conservatively folding only when no there 1988 // is no interleaved operation. 1989 if (bufferCast->getBlock() == this->getOperation()->getBlock() && 1990 bufferCast->getNextNode() == this->getOperation()) 1991 return bufferCast.tensor(); 1992 return {}; 1993 } 1994 1995 //===----------------------------------------------------------------------===// 1996 // TransposeOp 1997 //===----------------------------------------------------------------------===// 1998 1999 /// Build a strided memref type by applying `permutationMap` tp `memRefType`. 2000 static MemRefType inferTransposeResultType(MemRefType memRefType, 2001 AffineMap permutationMap) { 2002 auto rank = memRefType.getRank(); 2003 auto originalSizes = memRefType.getShape(); 2004 // Compute permuted sizes. 2005 SmallVector<int64_t, 4> sizes(rank, 0); 2006 for (auto en : llvm::enumerate(permutationMap.getResults())) 2007 sizes[en.index()] = 2008 originalSizes[en.value().cast<AffineDimExpr>().getPosition()]; 2009 2010 // Compute permuted strides. 2011 int64_t offset; 2012 SmallVector<int64_t, 4> strides; 2013 auto res = getStridesAndOffset(memRefType, strides, offset); 2014 assert(succeeded(res) && strides.size() == static_cast<unsigned>(rank)); 2015 (void)res; 2016 auto map = 2017 makeStridedLinearLayoutMap(strides, offset, memRefType.getContext()); 2018 map = permutationMap ? map.compose(permutationMap) : map; 2019 return MemRefType::Builder(memRefType).setShape(sizes).setAffineMaps(map); 2020 } 2021 2022 void TransposeOp::build(OpBuilder &b, OperationState &result, Value in, 2023 AffineMapAttr permutation, 2024 ArrayRef<NamedAttribute> attrs) { 2025 auto permutationMap = permutation.getValue(); 2026 assert(permutationMap); 2027 2028 auto memRefType = in.getType().cast<MemRefType>(); 2029 // Compute result type. 2030 MemRefType resultType = inferTransposeResultType(memRefType, permutationMap); 2031 2032 build(b, result, resultType, in, attrs); 2033 result.addAttribute(TransposeOp::getPermutationAttrName(), permutation); 2034 } 2035 2036 // transpose $in $permutation attr-dict : type($in) `to` type(results) 2037 static void print(OpAsmPrinter &p, TransposeOp op) { 2038 p << "memref.transpose " << op.in() << " " << op.permutation(); 2039 p.printOptionalAttrDict(op->getAttrs(), 2040 {TransposeOp::getPermutationAttrName()}); 2041 p << " : " << op.in().getType() << " to " << op.getType(); 2042 } 2043 2044 static ParseResult parseTransposeOp(OpAsmParser &parser, 2045 OperationState &result) { 2046 OpAsmParser::OperandType in; 2047 AffineMap permutation; 2048 MemRefType srcType, dstType; 2049 if (parser.parseOperand(in) || parser.parseAffineMap(permutation) || 2050 parser.parseOptionalAttrDict(result.attributes) || 2051 parser.parseColonType(srcType) || 2052 parser.resolveOperand(in, srcType, result.operands) || 2053 parser.parseKeywordType("to", dstType) || 2054 parser.addTypeToList(dstType, result.types)) 2055 return failure(); 2056 2057 result.addAttribute(TransposeOp::getPermutationAttrName(), 2058 AffineMapAttr::get(permutation)); 2059 return success(); 2060 } 2061 2062 static LogicalResult verify(TransposeOp op) { 2063 if (!op.permutation().isPermutation()) 2064 return op.emitOpError("expected a permutation map"); 2065 if (op.permutation().getNumDims() != op.getShapedType().getRank()) 2066 return op.emitOpError( 2067 "expected a permutation map of same rank as the input"); 2068 2069 auto srcType = op.in().getType().cast<MemRefType>(); 2070 auto dstType = op.getType().cast<MemRefType>(); 2071 auto transposedType = inferTransposeResultType(srcType, op.permutation()); 2072 if (dstType != transposedType) 2073 return op.emitOpError("output type ") 2074 << dstType << " does not match transposed input type " << srcType 2075 << ", " << transposedType; 2076 return success(); 2077 } 2078 2079 OpFoldResult TransposeOp::fold(ArrayRef<Attribute>) { 2080 if (succeeded(foldMemRefCast(*this))) 2081 return getResult(); 2082 return {}; 2083 } 2084 2085 //===----------------------------------------------------------------------===// 2086 // ViewOp 2087 //===----------------------------------------------------------------------===// 2088 2089 static ParseResult parseViewOp(OpAsmParser &parser, OperationState &result) { 2090 OpAsmParser::OperandType srcInfo; 2091 SmallVector<OpAsmParser::OperandType, 1> offsetInfo; 2092 SmallVector<OpAsmParser::OperandType, 4> sizesInfo; 2093 auto indexType = parser.getBuilder().getIndexType(); 2094 Type srcType, dstType; 2095 llvm::SMLoc offsetLoc; 2096 if (parser.parseOperand(srcInfo) || parser.getCurrentLocation(&offsetLoc) || 2097 parser.parseOperandList(offsetInfo, OpAsmParser::Delimiter::Square)) 2098 return failure(); 2099 2100 if (offsetInfo.size() != 1) 2101 return parser.emitError(offsetLoc) << "expects 1 offset operand"; 2102 2103 return failure( 2104 parser.parseOperandList(sizesInfo, OpAsmParser::Delimiter::Square) || 2105 parser.parseOptionalAttrDict(result.attributes) || 2106 parser.parseColonType(srcType) || 2107 parser.resolveOperand(srcInfo, srcType, result.operands) || 2108 parser.resolveOperands(offsetInfo, indexType, result.operands) || 2109 parser.resolveOperands(sizesInfo, indexType, result.operands) || 2110 parser.parseKeywordType("to", dstType) || 2111 parser.addTypeToList(dstType, result.types)); 2112 } 2113 2114 static void print(OpAsmPrinter &p, ViewOp op) { 2115 p << op.getOperationName() << ' ' << op.getOperand(0) << '['; 2116 p.printOperand(op.byte_shift()); 2117 p << "][" << op.sizes() << ']'; 2118 p.printOptionalAttrDict(op->getAttrs()); 2119 p << " : " << op.getOperand(0).getType() << " to " << op.getType(); 2120 } 2121 2122 static LogicalResult verify(ViewOp op) { 2123 auto baseType = op.getOperand(0).getType().cast<MemRefType>(); 2124 auto viewType = op.getType(); 2125 2126 // The base memref should have identity layout map (or none). 2127 if (baseType.getAffineMaps().size() > 1 || 2128 (baseType.getAffineMaps().size() == 1 && 2129 !baseType.getAffineMaps()[0].isIdentity())) 2130 return op.emitError("unsupported map for base memref type ") << baseType; 2131 2132 // The result memref should have identity layout map (or none). 2133 if (viewType.getAffineMaps().size() > 1 || 2134 (viewType.getAffineMaps().size() == 1 && 2135 !viewType.getAffineMaps()[0].isIdentity())) 2136 return op.emitError("unsupported map for result memref type ") << viewType; 2137 2138 // The base memref and the view memref should be in the same memory space. 2139 if (baseType.getMemorySpace() != viewType.getMemorySpace()) 2140 return op.emitError("different memory spaces specified for base memref " 2141 "type ") 2142 << baseType << " and view memref type " << viewType; 2143 2144 // Verify that we have the correct number of sizes for the result type. 2145 unsigned numDynamicDims = viewType.getNumDynamicDims(); 2146 if (op.sizes().size() != numDynamicDims) 2147 return op.emitError("incorrect number of size operands for type ") 2148 << viewType; 2149 2150 return success(); 2151 } 2152 2153 Value ViewOp::getViewSource() { return source(); } 2154 2155 namespace { 2156 2157 struct ViewOpShapeFolder : public OpRewritePattern<ViewOp> { 2158 using OpRewritePattern<ViewOp>::OpRewritePattern; 2159 2160 LogicalResult matchAndRewrite(ViewOp viewOp, 2161 PatternRewriter &rewriter) const override { 2162 // Return if none of the operands are constants. 2163 if (llvm::none_of(viewOp.getOperands(), [](Value operand) { 2164 return matchPattern(operand, matchConstantIndex()); 2165 })) 2166 return failure(); 2167 2168 // Get result memref type. 2169 auto memrefType = viewOp.getType(); 2170 2171 // Get offset from old memref view type 'memRefType'. 2172 int64_t oldOffset; 2173 SmallVector<int64_t, 4> oldStrides; 2174 if (failed(getStridesAndOffset(memrefType, oldStrides, oldOffset))) 2175 return failure(); 2176 assert(oldOffset == 0 && "Expected 0 offset"); 2177 2178 SmallVector<Value, 4> newOperands; 2179 2180 // Offset cannot be folded into result type. 2181 2182 // Fold any dynamic dim operands which are produced by a constant. 2183 SmallVector<int64_t, 4> newShapeConstants; 2184 newShapeConstants.reserve(memrefType.getRank()); 2185 2186 unsigned dynamicDimPos = 0; 2187 unsigned rank = memrefType.getRank(); 2188 for (unsigned dim = 0, e = rank; dim < e; ++dim) { 2189 int64_t dimSize = memrefType.getDimSize(dim); 2190 // If this is already static dimension, keep it. 2191 if (!ShapedType::isDynamic(dimSize)) { 2192 newShapeConstants.push_back(dimSize); 2193 continue; 2194 } 2195 auto *defOp = viewOp.sizes()[dynamicDimPos].getDefiningOp(); 2196 if (auto constantIndexOp = dyn_cast_or_null<ConstantIndexOp>(defOp)) { 2197 // Dynamic shape dimension will be folded. 2198 newShapeConstants.push_back(constantIndexOp.getValue()); 2199 } else { 2200 // Dynamic shape dimension not folded; copy operand from old memref. 2201 newShapeConstants.push_back(dimSize); 2202 newOperands.push_back(viewOp.sizes()[dynamicDimPos]); 2203 } 2204 dynamicDimPos++; 2205 } 2206 2207 // Create new memref type with constant folded dims. 2208 MemRefType newMemRefType = 2209 MemRefType::Builder(memrefType).setShape(newShapeConstants); 2210 // Nothing new, don't fold. 2211 if (newMemRefType == memrefType) 2212 return failure(); 2213 2214 // Create new ViewOp. 2215 auto newViewOp = rewriter.create<ViewOp>(viewOp.getLoc(), newMemRefType, 2216 viewOp.getOperand(0), 2217 viewOp.byte_shift(), newOperands); 2218 // Insert a cast so we have the same type as the old memref type. 2219 rewriter.replaceOpWithNewOp<CastOp>(viewOp, newViewOp, viewOp.getType()); 2220 return success(); 2221 } 2222 }; 2223 2224 struct ViewOpMemrefCastFolder : public OpRewritePattern<ViewOp> { 2225 using OpRewritePattern<ViewOp>::OpRewritePattern; 2226 2227 LogicalResult matchAndRewrite(ViewOp viewOp, 2228 PatternRewriter &rewriter) const override { 2229 Value memrefOperand = viewOp.getOperand(0); 2230 CastOp memrefCastOp = memrefOperand.getDefiningOp<CastOp>(); 2231 if (!memrefCastOp) 2232 return failure(); 2233 Value allocOperand = memrefCastOp.getOperand(); 2234 AllocOp allocOp = allocOperand.getDefiningOp<AllocOp>(); 2235 if (!allocOp) 2236 return failure(); 2237 rewriter.replaceOpWithNewOp<ViewOp>(viewOp, viewOp.getType(), allocOperand, 2238 viewOp.byte_shift(), viewOp.sizes()); 2239 return success(); 2240 } 2241 }; 2242 2243 } // end anonymous namespace 2244 2245 void ViewOp::getCanonicalizationPatterns(RewritePatternSet &results, 2246 MLIRContext *context) { 2247 results.add<ViewOpShapeFolder, ViewOpMemrefCastFolder>(context); 2248 } 2249 2250 //===----------------------------------------------------------------------===// 2251 // TableGen'd op method definitions 2252 //===----------------------------------------------------------------------===// 2253 2254 #define GET_OP_CLASSES 2255 #include "mlir/Dialect/MemRef/IR/MemRefOps.cpp.inc" 2256