1//===- MemRefOps.td - MemRef op definitions ----------------*- tablegen -*-===// 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#ifndef MEMREF_OPS 10#define MEMREF_OPS 11 12include "mlir/Dialect/Arithmetic/IR/ArithmeticBase.td" 13include "mlir/Dialect/MemRef/IR/MemRefBase.td" 14include "mlir/Interfaces/CastInterfaces.td" 15include "mlir/Interfaces/ControlFlowInterfaces.td" 16include "mlir/Interfaces/CopyOpInterface.td" 17include "mlir/Interfaces/SideEffectInterfaces.td" 18include "mlir/Interfaces/ViewLikeInterface.td" 19include "mlir/IR/SymbolInterfaces.td" 20 21/// A TypeAttr for memref types. 22def MemRefTypeAttr 23 : TypeAttrBase<"::mlir::MemRefType", "memref type attribute"> { 24 let constBuilderCall = "::mlir::TypeAttr::get($0)"; 25} 26 27class MemRef_Op<string mnemonic, list<Trait> traits = []> 28 : Op<MemRef_Dialect, mnemonic, traits>; 29 30// Base class for ops with static/dynamic offset, sizes and strides 31// attributes/arguments. 32class MemRef_OpWithOffsetSizesAndStrides<string mnemonic, 33 list<Trait> traits = []> 34 : MemRef_Op<mnemonic, traits> { 35 code extraBaseClassDeclaration = [{ 36 /// Returns the dynamic sizes for this subview operation if specified. 37 ::mlir::Operation::operand_range getDynamicSizes() { return getSizes(); } 38 39 /// Return the list of Range (i.e. offset, size, stride). Each 40 /// Range entry contains either the dynamic value or a ConstantIndexOp 41 /// constructed with `b` at location `loc`. 42 ::mlir::SmallVector<::mlir::Range, 8> getOrCreateRanges( 43 ::mlir::OpBuilder &b, ::mlir::Location loc) { 44 return ::mlir::getOrCreateRanges(*this, b, loc); 45 } 46 }]; 47} 48 49//===----------------------------------------------------------------------===// 50// AllocLikeOp 51//===----------------------------------------------------------------------===// 52 53// Base class for memref allocating ops: alloca and alloc. 54// 55// %0 = alloclike(%m)[%s] : memref<8x?xf32, affine_map<(d0, d1)[s0] -> (d0 + s0, d1)>> 56// 57class AllocLikeOp<string mnemonic, 58 Resource resource, 59 list<Trait> traits = []> : 60 MemRef_Op<mnemonic, 61 !listconcat([ 62 AttrSizedOperandSegments 63 ], traits)> { 64 65 let arguments = (ins Variadic<Index>:$dynamicSizes, 66 // The symbolic operands (the ones in square brackets) 67 // bind to the symbols of the memref's layout map. 68 Variadic<Index>:$symbolOperands, 69 Confined<OptionalAttr<I64Attr>, 70 [IntMinValue<0>]>:$alignment); 71 let results = (outs Res<AnyMemRef, "", [MemAlloc<resource>]>:$memref); 72 73 let builders = [ 74 OpBuilder<(ins "MemRefType":$memrefType, 75 CArg<"IntegerAttr", "IntegerAttr()">:$alignment), [{ 76 return build($_builder, $_state, memrefType, {}, alignment); 77 }]>, 78 OpBuilder<(ins "MemRefType":$memrefType, "ValueRange":$dynamicSizes, 79 CArg<"IntegerAttr", "IntegerAttr()">:$alignment), [{ 80 return build($_builder, $_state, memrefType, dynamicSizes, {}, alignment); 81 }]>, 82 OpBuilder<(ins "MemRefType":$memrefType, "ValueRange":$dynamicSizes, 83 "ValueRange":$symbolOperands, 84 CArg<"IntegerAttr", "{}">:$alignment), [{ 85 $_state.types.push_back(memrefType); 86 $_state.addOperands(dynamicSizes); 87 $_state.addOperands(symbolOperands); 88 $_state.addAttribute(getOperandSegmentSizeAttr(), 89 $_builder.getI32VectorAttr({ 90 static_cast<int32_t>(dynamicSizes.size()), 91 static_cast<int32_t>(symbolOperands.size())})); 92 if (alignment) 93 $_state.addAttribute(getAlignmentAttrStrName(), alignment); 94 }]>]; 95 96 let extraClassDeclaration = [{ 97 static StringRef getAlignmentAttrStrName() { return "alignment"; } 98 99 MemRefType getType() { return getResult().getType().cast<MemRefType>(); } 100 }]; 101 102 let assemblyFormat = [{ 103 `(`$dynamicSizes`)` (`` `[` $symbolOperands^ `]`)? attr-dict `:` type($memref) 104 }]; 105 106 let hasCanonicalizer = 1; 107 let hasVerifier = 1; 108} 109 110//===----------------------------------------------------------------------===// 111// AssumeAlignmentOp 112//===----------------------------------------------------------------------===// 113 114def AssumeAlignmentOp : MemRef_Op<"assume_alignment"> { 115 let summary = 116 "assertion that gives alignment information to the input memref"; 117 let description = [{ 118 The `assume_alignment` operation takes a memref and an integer of alignment 119 value, and internally annotates the buffer with the given alignment. If 120 the buffer isn't aligned to the given alignment, the behavior is undefined. 121 122 This operation doesn't affect the semantics of a correct program. It's for 123 optimization only, and the optimization is best-effort. 124 }]; 125 let arguments = (ins AnyMemRef:$memref, 126 Confined<I32Attr, [IntPositive]>:$alignment); 127 let results = (outs); 128 129 let assemblyFormat = "$memref `,` $alignment attr-dict `:` type($memref)"; 130 let hasVerifier = 1; 131} 132 133//===----------------------------------------------------------------------===// 134// AllocOp 135//===----------------------------------------------------------------------===// 136 137def MemRef_AllocOp : AllocLikeOp<"alloc", DefaultResource, []> { 138 let summary = "memory allocation operation"; 139 let description = [{ 140 The `alloc` operation allocates a region of memory, as specified by its 141 memref type. 142 143 Example: 144 145 ```mlir 146 %0 = memref.alloc() : memref<8x64xf32, 1> 147 ``` 148 149 The optional list of dimension operands are bound to the dynamic dimensions 150 specified in its memref type. In the example below, the ssa value '%d' is 151 bound to the second dimension of the memref (which is dynamic). 152 153 ```mlir 154 %0 = memref.alloc(%d) : memref<8x?xf32, 1> 155 ``` 156 157 The optional list of symbol operands are bound to the symbols of the 158 memrefs affine map. In the example below, the ssa value '%s' is bound to 159 the symbol 's0' in the affine map specified in the allocs memref type. 160 161 ```mlir 162 %0 = memref.alloc()[%s] : memref<8x64xf32, 163 affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1> 164 ``` 165 166 This operation returns a single ssa value of memref type, which can be used 167 by subsequent load and store operations. 168 169 The optional `alignment` attribute may be specified to ensure that the 170 region of memory that will be indexed is aligned at the specified byte 171 boundary. 172 173 ```mlir 174 %0 = memref.alloc()[%s] {alignment = 8} : 175 memref<8x64xf32, affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1> 176 ``` 177 }]; 178 let hasVerifier = 1; 179} 180 181//===----------------------------------------------------------------------===// 182// AllocaOp 183//===----------------------------------------------------------------------===// 184 185def MemRef_AllocaOp : AllocLikeOp<"alloca", AutomaticAllocationScopeResource> { 186 let summary = "stack memory allocation operation"; 187 let description = [{ 188 The `alloca` operation allocates memory on the stack, to be automatically 189 released when control transfers back from the region of its closest 190 surrounding operation with an 191 [`AutomaticAllocationScope`](../Traits.md/#automaticallocationscope) trait. 192 The amount of memory allocated is specified by its memref and additional 193 operands. For example: 194 195 ```mlir 196 %0 = memref.alloca() : memref<8x64xf32> 197 ``` 198 199 The optional list of dimension operands are bound to the dynamic dimensions 200 specified in its memref type. In the example below, the SSA value '%d' is 201 bound to the second dimension of the memref (which is dynamic). 202 203 ```mlir 204 %0 = memref.alloca(%d) : memref<8x?xf32> 205 ``` 206 207 The optional list of symbol operands are bound to the symbols of the 208 memref's affine map. In the example below, the SSA value '%s' is bound to 209 the symbol 's0' in the affine map specified in the allocs memref type. 210 211 ```mlir 212 %0 = memref.alloca()[%s] : memref<8x64xf32, 213 affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>> 214 ``` 215 216 This operation returns a single SSA value of memref type, which can be used 217 by subsequent load and store operations. An optional alignment attribute, if 218 specified, guarantees alignment at least to that boundary. If not specified, 219 an alignment on any convenient boundary compatible with the type will be 220 chosen. 221 }]; 222 let hasVerifier = 1; 223} 224 225//===----------------------------------------------------------------------===// 226// AllocaScopeOp 227//===----------------------------------------------------------------------===// 228 229def MemRef_AllocaScopeOp : MemRef_Op<"alloca_scope", 230 [AutomaticAllocationScope, 231 DeclareOpInterfaceMethods<RegionBranchOpInterface>, 232 SingleBlockImplicitTerminator<"AllocaScopeReturnOp">, 233 RecursiveSideEffects, 234 NoRegionArguments]> { 235 let summary = "explicitly delimited scope for stack allocation"; 236 let description = [{ 237 The `memref.alloca_scope` operation represents an explicitly-delimited 238 scope for the alloca allocations. Any `memref.alloca` operations that are 239 used within this scope are going to be cleaned up automatically once 240 the control-flow exits the nested region. For example: 241 242 ```mlir 243 memref.alloca_scope { 244 %myalloca = memref.alloca(): memref<4x3xf32> 245 ... 246 } 247 ``` 248 249 Here, `%myalloca` memref is valid within the explicitly delimited scope 250 and is automatically deallocated at the end of the given region. Conceptually, 251 `memref.alloca_scope` is a passthrough operation with 252 `AutomaticAllocationScope` that spans the body of the region within the operation. 253 254 `memref.alloca_scope` may also return results that are defined in the nested 255 region. To return a value, one should use `memref.alloca_scope.return` 256 operation: 257 258 ```mlir 259 %result = memref.alloca_scope { 260 ... 261 memref.alloca_scope.return %value 262 } 263 ``` 264 265 If `memref.alloca_scope` returns no value, the `memref.alloca_scope.return ` can 266 be left out, and will be inserted implicitly. 267 }]; 268 269 let results = (outs Variadic<AnyType>:$results); 270 let regions = (region SizedRegion<1>:$bodyRegion); 271 let hasCustomAssemblyFormat = 1; 272 let hasCanonicalizer = 1; 273} 274 275//===----------------------------------------------------------------------===// 276// AllocaScopeReturnOp 277//===----------------------------------------------------------------------===// 278 279def MemRef_AllocaScopeReturnOp : MemRef_Op<"alloca_scope.return", 280 [HasParent<"AllocaScopeOp">, 281 NoSideEffect, 282 ReturnLike, 283 Terminator]> { 284 let summary = "terminator for alloca_scope operation"; 285 let description = [{ 286 `memref.alloca_scope.return` operation returns zero or more SSA values 287 from the region within `memref.alloca_scope`. If no values are returned, 288 the return operation may be omitted. Otherwise, it has to be present 289 to indicate which values are going to be returned. For example: 290 291 ```mlir 292 memref.alloca_scope.return %value 293 ``` 294 }]; 295 296 let arguments = (ins Variadic<AnyType>:$results); 297 let builders = [OpBuilder<(ins), [{ /*nothing to do */ }]>]; 298 299 let assemblyFormat = "attr-dict ($results^ `:` type($results))?"; 300} 301 302//===----------------------------------------------------------------------===// 303// CastOp 304//===----------------------------------------------------------------------===// 305 306def MemRef_CastOp : MemRef_Op<"cast", [ 307 NoSideEffect, SameOperandsAndResultShape, 308 DeclareOpInterfaceMethods<CastOpInterface>, 309 ViewLikeOpInterface, 310 MemRefsNormalizable 311 ]> { 312 let summary = "memref cast operation"; 313 let description = [{ 314 Syntax: 315 316 ``` 317 operation ::= ssa-id `=` `memref.cast` ssa-use `:` type `to` type 318 ``` 319 320 The `memref.cast` operation converts a memref from one type to an equivalent 321 type with a compatible shape. The source and destination types are 322 compatible if: 323 324 a. Both are ranked memref types with the same element type, address space, 325 and rank and: 326 1. Both have the same layout or both have compatible strided layouts. 327 2. The individual sizes (resp. offset and strides in the case of strided 328 memrefs) may convert constant dimensions to dynamic dimensions and 329 vice-versa. 330 331 If the cast converts any dimensions from an unknown to a known size, then it 332 acts as an assertion that fails at runtime if the dynamic dimensions 333 disagree with resultant destination size. 334 335 Example: 336 337 ```mlir 338 // Assert that the input dynamic shape matches the destination static shape. 339 %2 = memref.cast %1 : memref<?x?xf32> to memref<4x4xf32> 340 // Erase static shape information, replacing it with dynamic information. 341 %3 = memref.cast %1 : memref<4xf32> to memref<?xf32> 342 343 // The same holds true for offsets and strides. 344 345 // Assert that the input dynamic shape matches the destination static stride. 346 %4 = memref.cast %1 : memref<12x4xf32, offset:?, strides: [?, ?]> to 347 memref<12x4xf32, offset:5, strides: [4, 1]> 348 // Erase static offset and stride information, replacing it with 349 // dynamic information. 350 %5 = memref.cast %1 : memref<12x4xf32, offset:5, strides: [4, 1]> to 351 memref<12x4xf32, offset:?, strides: [?, ?]> 352 ``` 353 354 b. Either or both memref types are unranked with the same element type, and 355 address space. 356 357 Example: 358 359 ```mlir 360 Cast to concrete shape. 361 %4 = memref.cast %1 : memref<*xf32> to memref<4x?xf32> 362 363 Erase rank information. 364 %5 = memref.cast %1 : memref<4x?xf32> to memref<*xf32> 365 ``` 366 }]; 367 368 let arguments = (ins AnyRankedOrUnrankedMemRef:$source); 369 let results = (outs AnyRankedOrUnrankedMemRef:$dest); 370 let assemblyFormat = "$source attr-dict `:` type($source) `to` type($dest)"; 371 372 let extraClassDeclaration = [{ 373 /// Fold the given CastOp into consumer op. 374 static bool canFoldIntoConsumerOp(CastOp castOp); 375 376 Value getViewSource() { return getSource(); } 377 }]; 378 379 let hasFolder = 1; 380} 381 382//===----------------------------------------------------------------------===// 383// CopyOp 384//===----------------------------------------------------------------------===// 385 386def CopyOp : MemRef_Op<"copy", 387 [CopyOpInterface, SameOperandsElementType, SameOperandsShape]> { 388 389 let description = [{ 390 Copies the data from the source to the destination memref. 391 392 Usage: 393 394 ```mlir 395 memref.copy %arg0, %arg1 : memref<?xf32> to memref<?xf32> 396 ``` 397 398 Source and destination are expected to have the same element type and shape. 399 Otherwise, the result is undefined. They may have different layouts. 400 }]; 401 402 let arguments = (ins Arg<AnyRankedOrUnrankedMemRef, "the memref to copy from", 403 [MemRead]>:$source, 404 Arg<AnyRankedOrUnrankedMemRef, "the memref to copy to", 405 [MemWrite]>:$target); 406 407 let assemblyFormat = [{ 408 $source `,` $target attr-dict `:` type($source) `to` type($target) 409 }]; 410 411 let hasCanonicalizer = 1; 412 let hasFolder = 1; 413} 414 415//===----------------------------------------------------------------------===// 416// DeallocOp 417//===----------------------------------------------------------------------===// 418 419def MemRef_DeallocOp : MemRef_Op<"dealloc", [MemRefsNormalizable]> { 420 let summary = "memory deallocation operation"; 421 let description = [{ 422 The `dealloc` operation frees the region of memory referenced by a memref 423 which was originally created by the `alloc` operation. 424 The `dealloc` operation should not be called on memrefs which alias an 425 alloc'd memref (e.g. memrefs returned by `view` operations). 426 427 Example: 428 429 ```mlir 430 %0 = memref.alloc() : memref<8x64xf32, affine_map<(d0, d1) -> (d0, d1), 1>> 431 memref.dealloc %0 : memref<8x64xf32, affine_map<(d0, d1) -> (d0, d1), 1>> 432 ``` 433 }]; 434 435 let arguments = (ins Arg<AnyRankedOrUnrankedMemRef, "", [MemFree]>:$memref); 436 437 let hasFolder = 1; 438 let assemblyFormat = "$memref attr-dict `:` type($memref)"; 439} 440 441//===----------------------------------------------------------------------===// 442// DimOp 443//===----------------------------------------------------------------------===// 444 445def MemRef_DimOp : MemRef_Op<"dim", [NoSideEffect, MemRefsNormalizable]> { 446 let summary = "dimension index operation"; 447 let description = [{ 448 The `dim` operation takes a memref and a dimension operand of type `index`. 449 It returns the size of the requested dimension of the given memref. 450 If the dimension index is out of bounds the behavior is undefined. 451 452 The specified memref type is that of the first operand. 453 454 Example: 455 456 ```mlir 457 // Always returns 4, can be constant folded: 458 %c0 = arith.constant 0 : index 459 %x = memref.dim %A, %c0 : memref<4 x ? x f32> 460 461 // Returns the dynamic dimension of %A. 462 %c1 = arith.constant 1 : index 463 %y = memref.dim %A, %c1 : memref<4 x ? x f32> 464 465 // Equivalent generic form: 466 %x = "memref.dim"(%A, %c0) : (memref<4 x ? x f32>, index) -> index 467 %y = "memref.dim"(%A, %c1) : (memref<4 x ? x f32>, index) -> index 468 ``` 469 }]; 470 471 let arguments = (ins AnyRankedOrUnrankedMemRef:$source, 472 Index:$index); 473 let results = (outs Index:$result); 474 475 let assemblyFormat = [{ 476 attr-dict $source `,` $index `:` type($source) 477 }]; 478 479 let builders = [ 480 OpBuilder<(ins "Value":$source, "int64_t":$index)>, 481 OpBuilder<(ins "Value":$source, "Value":$index)> 482 ]; 483 484 let extraClassDeclaration = [{ 485 /// Helper function to get the index as a simple integer if it is constant. 486 Optional<int64_t> getConstantIndex(); 487 }]; 488 489 let hasCanonicalizer = 1; 490 let hasFolder = 1; 491 let hasVerifier = 1; 492} 493 494//===----------------------------------------------------------------------===// 495// DmaStartOp 496//===----------------------------------------------------------------------===// 497 498def MemRef_DmaStartOp : MemRef_Op<"dma_start"> { 499 let summary = "non-blocking DMA operation that starts a transfer"; 500 let description = [{ 501 DmaStartOp starts a non-blocking DMA operation that transfers data from a 502 source memref to a destination memref. The source and destination memref 503 need not be of the same dimensionality, but need to have the same elemental 504 type. The operands include the source and destination memref's each followed 505 by its indices, size of the data transfer in terms of the number of elements 506 (of the elemental type of the memref), a tag memref with its indices, and 507 optionally at the end, a stride and a number_of_elements_per_stride 508 arguments. The tag location is used by a DmaWaitOp to check for completion. 509 The indices of the source memref, destination memref, and the tag memref 510 have the same restrictions as any load/store. The optional stride arguments 511 should be of 'index' type, and specify a stride for the slower memory space 512 (memory space with a lower memory space id), transferring chunks of 513 number_of_elements_per_stride every stride until %num_elements are 514 transferred. Either both or no stride arguments should be specified. If the 515 source and destination locations overlap the behavior of this operation is 516 not defined. 517 518 For example, a DmaStartOp operation that transfers 256 elements of a memref 519 '%src' in memory space 0 at indices [%i, %j] to memref '%dst' in memory 520 space 1 at indices [%k, %l], would be specified as follows: 521 522 ```mlir 523 %num_elements = arith.constant 256 524 %idx = arith.constant 0 : index 525 %tag = memref.alloc() : memref<1 x i32, affine_map<(d0) -> (d0)>, 4> 526 dma_start %src[%i, %j], %dst[%k, %l], %num_elements, %tag[%idx] : 527 memref<40 x 128 x f32>, affine_map<(d0) -> (d0)>, 0>, 528 memref<2 x 1024 x f32>, affine_map<(d0) -> (d0)>, 1>, 529 memref<1 x i32>, affine_map<(d0) -> (d0)>, 2> 530 ``` 531 532 If %stride and %num_elt_per_stride are specified, the DMA is expected to 533 transfer %num_elt_per_stride elements every %stride elements apart from 534 memory space 0 until %num_elements are transferred. 535 536 ```mlir 537 dma_start %src[%i, %j], %dst[%k, %l], %num_elements, %tag[%idx], %stride, 538 %num_elt_per_stride : 539 ``` 540 541 TODO: add additional operands to allow source and destination striding, and 542 multiple stride levels. 543 TODO: Consider replacing src/dst memref indices with view memrefs. 544 }]; 545 let arguments = (ins Variadic<AnyType>:$operands); 546 547 let builders = [ 548 OpBuilder<(ins "Value":$srcMemRef, "ValueRange":$srcIndices, 549 "Value":$destMemRef, "ValueRange":$destIndices, 550 "Value":$numElements, "Value":$tagMemRef, 551 "ValueRange":$tagIndices, CArg<"Value", "{}">:$stride, 552 CArg<"Value", "{}">:$elementsPerStride)> 553 ]; 554 555 let extraClassDeclaration = [{ 556 // Returns the source MemRefType for this DMA operation. 557 Value getSrcMemRef() { return getOperand(0); } 558 // Returns the rank (number of indices) of the source MemRefType. 559 unsigned getSrcMemRefRank() { 560 return getSrcMemRef().getType().cast<MemRefType>().getRank(); 561 } 562 // Returns the source memref indices for this DMA operation. 563 operand_range getSrcIndices() { 564 return {(*this)->operand_begin() + 1, 565 (*this)->operand_begin() + 1 + getSrcMemRefRank()}; 566 } 567 568 // Returns the destination MemRefType for this DMA operations. 569 Value getDstMemRef() { return getOperand(1 + getSrcMemRefRank()); } 570 // Returns the rank (number of indices) of the destination MemRefType. 571 unsigned getDstMemRefRank() { 572 return getDstMemRef().getType().cast<MemRefType>().getRank(); 573 } 574 unsigned getSrcMemorySpace() { 575 return getSrcMemRef().getType().cast<MemRefType>().getMemorySpaceAsInt(); 576 } 577 unsigned getDstMemorySpace() { 578 return getDstMemRef().getType().cast<MemRefType>().getMemorySpaceAsInt(); 579 } 580 581 // Returns the destination memref indices for this DMA operation. 582 operand_range getDstIndices() { 583 return {(*this)->operand_begin() + 1 + getSrcMemRefRank() + 1, 584 (*this)->operand_begin() + 1 + getSrcMemRefRank() + 1 + 585 getDstMemRefRank()}; 586 } 587 588 // Returns the number of elements being transferred by this DMA operation. 589 Value getNumElements() { 590 return getOperand(1 + getSrcMemRefRank() + 1 + getDstMemRefRank()); 591 } 592 593 // Returns the Tag MemRef for this DMA operation. 594 Value getTagMemRef() { 595 return getOperand(1 + getSrcMemRefRank() + 1 + getDstMemRefRank() + 1); 596 } 597 598 // Returns the rank (number of indices) of the tag MemRefType. 599 unsigned getTagMemRefRank() { 600 return getTagMemRef().getType().cast<MemRefType>().getRank(); 601 } 602 603 // Returns the tag memref index for this DMA operation. 604 operand_range getTagIndices() { 605 unsigned tagIndexStartPos = 606 1 + getSrcMemRefRank() + 1 + getDstMemRefRank() + 1 + 1; 607 return {(*this)->operand_begin() + tagIndexStartPos, 608 (*this)->operand_begin() + tagIndexStartPos + getTagMemRefRank()}; 609 } 610 611 /// Returns true if this is a DMA from a faster memory space to a slower 612 /// one. 613 bool isDestMemorySpaceFaster() { 614 return (getSrcMemorySpace() < getDstMemorySpace()); 615 } 616 617 /// Returns true if this is a DMA from a slower memory space to a faster 618 /// one. 619 bool isSrcMemorySpaceFaster() { 620 // Assumes that a lower number is for a slower memory space. 621 return (getDstMemorySpace() < getSrcMemorySpace()); 622 } 623 624 /// Given a DMA start operation, returns the operand position of either the 625 /// source or destination memref depending on the one that is at the higher 626 /// level of the memory hierarchy. Asserts failure if neither is true. 627 unsigned getFasterMemPos() { 628 assert(isSrcMemorySpaceFaster() || isDestMemorySpaceFaster()); 629 return isSrcMemorySpaceFaster() ? 0 : getSrcMemRefRank() + 1; 630 } 631 632 bool isStrided() { 633 return getNumOperands() != 1 + getSrcMemRefRank() + 1 + 634 getDstMemRefRank() + 1 + 1 + 635 getTagMemRefRank(); 636 } 637 638 Value getStride() { 639 if (!isStrided()) 640 return nullptr; 641 return getOperand(getNumOperands() - 1 - 1); 642 } 643 644 Value getNumElementsPerStride() { 645 if (!isStrided()) 646 return nullptr; 647 return getOperand(getNumOperands() - 1); 648 } 649 }]; 650 let hasCustomAssemblyFormat = 1; 651 let hasFolder = 1; 652 let hasVerifier = 1; 653} 654 655//===----------------------------------------------------------------------===// 656// DmaWaitOp 657//===----------------------------------------------------------------------===// 658 659def MemRef_DmaWaitOp : MemRef_Op<"dma_wait"> { 660 let summary = "blocking DMA operation that waits for transfer completion"; 661 let description = [{ 662 DmaWaitOp blocks until the completion of a DMA operation associated with the 663 tag element '%tag[%index]'. %tag is a memref, and %index has to be an index 664 with the same restrictions as any load/store index. %num_elements is the 665 number of elements associated with the DMA operation. 666 667 Example: 668 669 ```mlir 670 dma_start %src[%i, %j], %dst[%k, %l], %num_elements, %tag[%index] : 671 memref<2048 x f32>, affine_map<(d0) -> (d0)>, 0>, 672 memref<256 x f32>, affine_map<(d0) -> (d0)>, 1> 673 memref<1 x i32>, affine_map<(d0) -> (d0)>, 2> 674 ... 675 ... 676 dma_wait %tag[%index], %num_elements : memref<1 x i32, affine_map<(d0) -> (d0)>, 2> 677 ``` 678 }]; 679 let arguments = (ins AnyMemRef:$tagMemRef, 680 Variadic<Index>:$tagIndices, 681 Index:$numElements); 682 let assemblyFormat = [{ 683 $tagMemRef `[` $tagIndices `]` `,` $numElements attr-dict `:` 684 type($tagMemRef) 685 }]; 686 let extraClassDeclaration = [{ 687 /// Returns the rank (number of indices) of the tag memref. 688 unsigned getTagMemRefRank() { 689 return getTagMemRef().getType().cast<MemRefType>().getRank(); 690 } 691 }]; 692 let hasFolder = 1; 693 let hasVerifier = 1; 694} 695 696//===----------------------------------------------------------------------===// 697// GenericAtomicRMWOp 698//===----------------------------------------------------------------------===// 699 700def GenericAtomicRMWOp : MemRef_Op<"generic_atomic_rmw", [ 701 SingleBlockImplicitTerminator<"AtomicYieldOp">, 702 TypesMatchWith<"result type matches element type of memref", 703 "memref", "result", 704 "$_self.cast<MemRefType>().getElementType()"> 705 ]> { 706 let summary = "atomic read-modify-write operation with a region"; 707 let description = [{ 708 The `memref.generic_atomic_rmw` operation provides a way to perform a 709 read-modify-write sequence that is free from data races. The memref operand 710 represents the buffer that the read and write will be performed against, as 711 accessed by the specified indices. The arity of the indices is the rank of 712 the memref. The result represents the latest value that was stored. The 713 region contains the code for the modification itself. The entry block has 714 a single argument that represents the value stored in `memref[indices]` 715 before the write is performed. No side-effecting ops are allowed in the 716 body of `GenericAtomicRMWOp`. 717 718 Example: 719 720 ```mlir 721 %x = memref.generic_atomic_rmw %I[%i] : memref<10xf32> { 722 ^bb0(%current_value : f32): 723 %c1 = arith.constant 1.0 : f32 724 %inc = arith.addf %c1, %current_value : f32 725 memref.atomic_yield %inc : f32 726 } 727 ``` 728 }]; 729 730 let arguments = (ins 731 MemRefOf<[AnySignlessInteger, AnyFloat]>:$memref, 732 Variadic<Index>:$indices); 733 734 let results = (outs 735 AnyTypeOf<[AnySignlessInteger, AnyFloat]>:$result); 736 737 let regions = (region AnyRegion:$atomic_body); 738 739 let skipDefaultBuilders = 1; 740 let builders = [OpBuilder<(ins "Value":$memref, "ValueRange":$ivs)>]; 741 742 let extraClassDeclaration = [{ 743 // TODO: remove post migrating callers. 744 Region &body() { return getRegion(); } 745 746 // The value stored in memref[ivs]. 747 Value getCurrentValue() { 748 return getRegion().getArgument(0); 749 } 750 MemRefType getMemRefType() { 751 return getMemref().getType().cast<MemRefType>(); 752 } 753 }]; 754 let hasCustomAssemblyFormat = 1; 755 let hasVerifier = 1; 756} 757 758def AtomicYieldOp : MemRef_Op<"atomic_yield", [ 759 HasParent<"GenericAtomicRMWOp">, 760 NoSideEffect, 761 Terminator 762 ]> { 763 let summary = "yield operation for GenericAtomicRMWOp"; 764 let description = [{ 765 "memref.atomic_yield" yields an SSA value from a 766 GenericAtomicRMWOp region. 767 }]; 768 769 let arguments = (ins AnyType:$result); 770 let assemblyFormat = "$result attr-dict `:` type($result)"; 771 let hasVerifier = 1; 772} 773 774//===----------------------------------------------------------------------===// 775// GetGlobalOp 776//===----------------------------------------------------------------------===// 777 778def MemRef_GetGlobalOp : MemRef_Op<"get_global", 779 [NoSideEffect, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> { 780 let summary = "get the memref pointing to a global variable"; 781 let description = [{ 782 The `memref.get_global` operation retrieves the memref pointing to a 783 named global variable. If the global variable is marked constant, writing 784 to the result memref (such as through a `memref.store` operation) is 785 undefined. 786 787 Example: 788 789 ```mlir 790 %x = memref.get_global @foo : memref<2xf32> 791 ``` 792 }]; 793 794 let arguments = (ins FlatSymbolRefAttr:$name); 795 let results = (outs AnyStaticShapeMemRef:$result); 796 let assemblyFormat = "$name `:` type($result) attr-dict"; 797} 798 799//===----------------------------------------------------------------------===// 800// GlobalOp 801//===----------------------------------------------------------------------===// 802 803def MemRef_GlobalOp : MemRef_Op<"global", [Symbol]> { 804 let summary = "declare or define a global memref variable"; 805 let description = [{ 806 The `memref.global` operation declares or defines a named global memref 807 variable. The backing memory for the variable is allocated statically and is 808 described by the type of the variable (which should be a statically shaped 809 memref type). The operation is a declaration if no `initial_value` is 810 specified, else it is a definition. The `initial_value` can either be a unit 811 attribute to represent a definition of an uninitialized global variable, or 812 an elements attribute to represent the definition of a global variable with 813 an initial value. The global variable can also be marked constant using the 814 `constant` unit attribute. Writing to such constant global variables is 815 undefined. 816 817 The global variable can be accessed by using the `memref.get_global` to 818 retrieve the memref for the global variable. Note that the memref 819 for such global variable itself is immutable (i.e., memref.get_global for a 820 given global variable will always return the same memref descriptor). 821 822 Example: 823 824 ```mlir 825 // Private variable with an initial value. 826 memref.global "private" @x : memref<2xf32> = dense<0.0,2.0> 827 828 // Private variable with an initial value and an alignment (power of 2). 829 memref.global "private" @x : memref<2xf32> = dense<0.0,2.0> {alignment = 64} 830 831 // Declaration of an external variable. 832 memref.global "private" @y : memref<4xi32> 833 834 // Uninitialized externally visible variable. 835 memref.global @z : memref<3xf16> = uninitialized 836 837 // Externally visible constant variable. 838 memref.global constant @c : memref<2xi32> = dense<1, 4> 839 ``` 840 }]; 841 842 let arguments = (ins SymbolNameAttr:$sym_name, 843 OptionalAttr<StrAttr>:$sym_visibility, 844 MemRefTypeAttr:$type, 845 OptionalAttr<AnyAttr>:$initial_value, 846 UnitAttr:$constant, 847 OptionalAttr<I64Attr>:$alignment); 848 849 let assemblyFormat = [{ 850 ($sym_visibility^)? 851 (`constant` $constant^)? 852 $sym_name `:` 853 custom<GlobalMemrefOpTypeAndInitialValue>($type, $initial_value) 854 attr-dict 855 }]; 856 857 let extraClassDeclaration = [{ 858 bool isExternal() { return !getInitialValue(); } 859 bool isUninitialized() { 860 return !isExternal() && getInitialValue()->isa<UnitAttr>(); 861 } 862 /// Returns the constant initial value if the memref.global is a constant, 863 /// or null otherwise. 864 ElementsAttr getConstantInitValue(); 865 }]; 866 let hasVerifier = 1; 867} 868 869//===----------------------------------------------------------------------===// 870// LoadOp 871//===----------------------------------------------------------------------===// 872 873def LoadOp : MemRef_Op<"load", 874 [TypesMatchWith<"result type matches element type of 'memref'", 875 "memref", "result", 876 "$_self.cast<MemRefType>().getElementType()">, 877 MemRefsNormalizable]> { 878 let summary = "load operation"; 879 let description = [{ 880 The `load` op reads an element from a memref specified by an index list. The 881 output of load is a new value with the same type as the elements of the 882 memref. The arity of indices is the rank of the memref (i.e., if the memref 883 loaded from is of rank 3, then 3 indices are required for the load following 884 the memref identifier). 885 886 In an `affine.if` or `affine.for` body, the indices of a load are restricted 887 to SSA values bound to surrounding loop induction variables, 888 [symbols](Affine.md/#dimensions-and-symbols), results of a 889 constant operations, or the result of an 890 `affine.apply` operation that can in turn take as arguments all of the 891 aforementioned SSA values or the recursively result of such an 892 `affine.apply` operation. 893 894 Example: 895 896 ```mlir 897 %1 = affine.apply affine_map<(d0, d1) -> (3*d0)> (%i, %j) 898 %2 = affine.apply affine_map<(d0, d1) -> (d1+1)> (%i, %j) 899 %12 = memref.load %A[%1, %2] : memref<8x?xi32, #layout, memspace0> 900 901 // Example of an indirect load (treated as non-affine) 902 %3 = affine.apply affine_map<(d0) -> (2*d0 + 1)>(%12) 903 %13 = memref.load %A[%3, %2] : memref<4x?xi32, #layout, memspace0> 904 ``` 905 906 **Context:** The `load` and `store` operations are specifically crafted to 907 fully resolve a reference to an element of a memref, and (in affine 908 `affine.if` and `affine.for` operations) the compiler can follow use-def 909 chains (e.g. through [`affine.apply`](Affine.md/#affineapply-affineapplyop) 910 operations) to precisely analyze references at compile-time using polyhedral 911 techniques. This is possible because of the 912 [restrictions on dimensions and symbols](Affine.md/#restrictions-on-dimensions-and-symbols) 913 in these contexts. 914 }]; 915 916 let arguments = (ins Arg<AnyMemRef, "the reference to load from", 917 [MemRead]>:$memref, 918 Variadic<Index>:$indices); 919 let results = (outs AnyType:$result); 920 921 let builders = [ 922 OpBuilder<(ins "Value":$memref, CArg<"ValueRange", "{}">:$indices), [{ 923 auto memrefType = memref.getType().cast<MemRefType>(); 924 $_state.addOperands(memref); 925 $_state.addOperands(indices); 926 $_state.types.push_back(memrefType.getElementType()); 927 }]>]; 928 929 let extraClassDeclaration = [{ 930 Value getMemRef() { return getOperand(0); } 931 void setMemRef(Value value) { setOperand(0, value); } 932 MemRefType getMemRefType() { 933 return getMemRef().getType().cast<MemRefType>(); 934 } 935 }]; 936 937 let hasFolder = 1; 938 let hasVerifier = 1; 939 940 let assemblyFormat = "$memref `[` $indices `]` attr-dict `:` type($memref)"; 941} 942 943//===----------------------------------------------------------------------===// 944// PrefetchOp 945//===----------------------------------------------------------------------===// 946 947def MemRef_PrefetchOp : MemRef_Op<"prefetch"> { 948 let summary = "prefetch operation"; 949 let description = [{ 950 The "prefetch" op prefetches data from a memref location described with 951 subscript indices similar to memref.load, and with three attributes: a 952 read/write specifier, a locality hint, and a cache type specifier as shown 953 below: 954 955 ```mlir 956 memref.prefetch %0[%i, %j], read, locality<3>, data : memref<400x400xi32> 957 ``` 958 959 The read/write specifier is either 'read' or 'write', the locality hint 960 ranges from locality<0> (no locality) to locality<3> (extremely local keep 961 in cache). The cache type specifier is either 'data' or 'instr' 962 and specifies whether the prefetch is performed on data cache or on 963 instruction cache. 964 }]; 965 966 let arguments = (ins AnyMemRef:$memref, Variadic<Index>:$indices, 967 BoolAttr:$isWrite, 968 Confined<I32Attr, [IntMinValue<0>, 969 IntMaxValue<3>]>:$localityHint, 970 BoolAttr:$isDataCache); 971 972 let extraClassDeclaration = [{ 973 MemRefType getMemRefType() { 974 return getMemref().getType().cast<MemRefType>(); 975 } 976 static StringRef getLocalityHintAttrStrName() { return "localityHint"; } 977 static StringRef getIsWriteAttrStrName() { return "isWrite"; } 978 static StringRef getIsDataCacheAttrStrName() { return "isDataCache"; } 979 }]; 980 981 let hasCustomAssemblyFormat = 1; 982 let hasFolder = 1; 983 let hasVerifier = 1; 984} 985 986//===----------------------------------------------------------------------===// 987// ReinterpretCastOp 988//===----------------------------------------------------------------------===// 989 990def MemRef_ReinterpretCastOp 991 : MemRef_OpWithOffsetSizesAndStrides<"reinterpret_cast", [ 992 NoSideEffect, AttrSizedOperandSegments, ViewLikeOpInterface, 993 OffsetSizeAndStrideOpInterface, MemRefsNormalizable 994 ]> { 995 let summary = "memref reinterpret cast operation"; 996 let description = [{ 997 Modify offset, sizes and strides of an unranked/ranked memref. 998 999 Example: 1000 ```mlir 1001 memref.reinterpret_cast %ranked to 1002 offset: [0], 1003 sizes: [%size0, 10], 1004 strides: [1, %stride1] 1005 : memref<?x?xf32> to memref<?x10xf32, offset: 0, strides: [1, ?]> 1006 1007 memref.reinterpret_cast %unranked to 1008 offset: [%offset], 1009 sizes: [%size0, %size1], 1010 strides: [%stride0, %stride1] 1011 : memref<*xf32> to memref<?x?xf32, offset: ?, strides: [?, ?]> 1012 ``` 1013 }]; 1014 1015 let arguments = (ins Arg<AnyRankedOrUnrankedMemRef, "", []>:$source, 1016 Variadic<Index>:$offsets, 1017 Variadic<Index>:$sizes, 1018 Variadic<Index>:$strides, 1019 I64ArrayAttr:$static_offsets, 1020 I64ArrayAttr:$static_sizes, 1021 I64ArrayAttr:$static_strides); 1022 let results = (outs AnyMemRef:$result); 1023 1024 let assemblyFormat = [{ 1025 $source `to` `offset` `` `:` 1026 custom<OperandsOrIntegersOffsetsOrStridesList>($offsets, $static_offsets) 1027 `` `,` `sizes` `` `:` 1028 custom<OperandsOrIntegersSizesList>($sizes, $static_sizes) `` `,` `strides` 1029 `` `:` 1030 custom<OperandsOrIntegersOffsetsOrStridesList>($strides, $static_strides) 1031 attr-dict `:` type($source) `to` type($result) 1032 }]; 1033 1034 let hasVerifier = 1; 1035 1036 let builders = [ 1037 // Build a ReinterpretCastOp with mixed static and dynamic entries. 1038 OpBuilder<(ins "MemRefType":$resultType, "Value":$source, 1039 "OpFoldResult":$offset, "ArrayRef<OpFoldResult>":$sizes, 1040 "ArrayRef<OpFoldResult>":$strides, 1041 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 1042 // Build a ReinterpretCastOp with static entries. 1043 OpBuilder<(ins "MemRefType":$resultType, "Value":$source, 1044 "int64_t":$offset, "ArrayRef<int64_t>":$sizes, 1045 "ArrayRef<int64_t>":$strides, 1046 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 1047 // Build a ReinterpretCastOp with dynamic entries. 1048 OpBuilder<(ins "MemRefType":$resultType, "Value":$source, 1049 "Value":$offset, "ValueRange":$sizes, 1050 "ValueRange":$strides, 1051 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)> 1052 ]; 1053 1054 let extraClassDeclaration = extraBaseClassDeclaration # [{ 1055 // The result of the op is always a ranked memref. 1056 MemRefType getType() { return getResult().getType().cast<MemRefType>(); } 1057 Value getViewSource() { return getSource(); } 1058 1059 /// Return the rank of the source ShapedType. 1060 unsigned getResultRank() { 1061 return getResult().getType().cast<ShapedType>().getRank(); 1062 } 1063 1064 /// Return the expected rank of each of the`static_offsets`, `static_sizes` 1065 /// and `static_strides` attributes. 1066 std::array<unsigned, 3> getArrayAttrMaxRanks() { 1067 unsigned resultRank = getResult().getType().cast<ShapedType>().getRank(); 1068 return {1, resultRank, resultRank}; 1069 } 1070 1071 /// Return the number of leading operands before the `offsets`, `sizes` and 1072 /// and `strides` operands. 1073 static unsigned getOffsetSizeAndStrideStartOperandIndex() { return 1; } 1074 }]; 1075 1076 let hasFolder = 1; 1077} 1078 1079//===----------------------------------------------------------------------===// 1080// RankOp 1081//===----------------------------------------------------------------------===// 1082 1083def MemRef_RankOp : MemRef_Op<"rank", [NoSideEffect]> { 1084 let summary = "rank operation"; 1085 let description = [{ 1086 The `memref.rank` operation takes a memref operand and returns its rank. 1087 1088 Example: 1089 1090 ```mlir 1091 %0 = memref.rank %arg0 : memref<*xf32> 1092 %1 = memref.rank %arg1 : memref<?x?xf32> 1093 ``` 1094 }]; 1095 1096 let arguments = (ins AnyRankedOrUnrankedMemRef:$memref); 1097 let results = (outs Index); 1098 1099 let hasFolder = 1; 1100 let assemblyFormat = "$memref attr-dict `:` type($memref)"; 1101} 1102 1103//===----------------------------------------------------------------------===// 1104// ReshapeOp 1105//===----------------------------------------------------------------------===// 1106 1107def MemRef_ReshapeOp: MemRef_Op<"reshape", [ 1108 ViewLikeOpInterface, NoSideEffect]> { 1109 let summary = "memref reshape operation"; 1110 let description = [{ 1111 The `reshape` operation converts a memref from one type to an 1112 equivalent type with a provided shape. The data is never copied or 1113 modified. The source and destination types are compatible if both have the 1114 same element type, same number of elements, address space and identity 1115 layout map. The following combinations are possible: 1116 1117 a. Source type is ranked or unranked. Shape argument has static size. 1118 Result type is ranked. 1119 1120 ```mlir 1121 // Reshape statically-shaped memref. 1122 %dst = memref.reshape %src(%shape) 1123 : (memref<4x1xf32>, memref<1xi32>) to memref<4xf32> 1124 %dst0 = memref.reshape %src(%shape0) 1125 : (memref<4x1xf32>, memref<2xi32>) to memref<2x2xf32> 1126 // Flatten unranked memref. 1127 %dst = memref.reshape %src(%shape) 1128 : (memref<*xf32>, memref<1xi32>) to memref<?xf32> 1129 ``` 1130 1131 b. Source type is ranked or unranked. Shape argument has dynamic size. 1132 Result type is unranked. 1133 1134 ```mlir 1135 // Reshape dynamically-shaped 1D memref. 1136 %dst = memref.reshape %src(%shape) 1137 : (memref<?xf32>, memref<?xi32>) to memref<*xf32> 1138 // Reshape unranked memref. 1139 %dst = memref.reshape %src(%shape) 1140 : (memref<*xf32>, memref<?xi32>) to memref<*xf32> 1141 ``` 1142 }]; 1143 1144 let arguments = (ins AnyRankedOrUnrankedMemRef:$source, 1145 MemRefRankOf<[AnySignlessInteger, Index], [1]>:$shape); 1146 let results = (outs AnyRankedOrUnrankedMemRef:$result); 1147 1148 let builders = [OpBuilder< 1149 (ins "MemRefType":$resultType, "Value":$operand, "Value":$shape), [{ 1150 $_state.addOperands(operand); 1151 $_state.addOperands(shape); 1152 $_state.addTypes(resultType); 1153 }]>]; 1154 1155 let extraClassDeclaration = [{ 1156 MemRefType getType() { return getResult().getType().cast<MemRefType>(); } 1157 Value getViewSource() { return getSource(); } 1158 }]; 1159 1160 let assemblyFormat = [{ 1161 $source `(` $shape `)` attr-dict `:` functional-type(operands, results) 1162 }]; 1163 let hasVerifier = 1; 1164} 1165 1166//===----------------------------------------------------------------------===// 1167// ExpandShapeOp / CollapseShapeOp 1168//===----------------------------------------------------------------------===// 1169 1170class MemRef_ReassociativeReshapeOp<string mnemonic, list<Trait> traits = []> : 1171 MemRef_Op<mnemonic, !listconcat(traits, 1172 [NoSideEffect, ViewLikeOpInterface])>, 1173 Arguments<(ins AnyStridedMemRef:$src, IndexListArrayAttr:$reassociation)>, 1174 Results<(outs AnyStridedMemRef:$result)>{ 1175 1176 code commonExtraClassDeclaration = [{ 1177 SmallVector<AffineMap, 4> getReassociationMaps(); 1178 1179 SmallVector<ReassociationExprs, 4> getReassociationExprs(); 1180 1181 SmallVector<ReassociationIndices, 4> getReassociationIndices() { 1182 SmallVector<ReassociationIndices, 4> reassociationIndices; 1183 for (auto attr : getReassociation()) 1184 reassociationIndices.push_back(llvm::to_vector<2>( 1185 llvm::map_range(attr.cast<ArrayAttr>(), [&](Attribute indexAttr) { 1186 return indexAttr.cast<IntegerAttr>().getInt(); 1187 }))); 1188 return reassociationIndices; 1189 }; 1190 1191 MemRefType getSrcType() { return getSrc().getType().cast<MemRefType>(); } 1192 1193 MemRefType getResultType() { return getResult().getType().cast<MemRefType>(); } 1194 1195 Value getViewSource() { return getSrc(); } 1196 }]; 1197 1198 let assemblyFormat = [{ 1199 $src $reassociation attr-dict `:` type($src) `into` type($result) 1200 }]; 1201 1202 let hasFolder = 1; 1203 let hasCanonicalizer = 1; 1204 let hasVerifier = 1; 1205} 1206 1207def MemRef_ExpandShapeOp : MemRef_ReassociativeReshapeOp<"expand_shape"> { 1208 let summary = "operation to produce a memref with a higher rank."; 1209 let description = [{ 1210 The `memref.expand_shape` op produces a new view with a higher rank whose 1211 sizes are a reassociation of the original `view`. The operation is limited 1212 to such reassociations, where a dimension is expanded into one or multiple 1213 contiguous dimensions. Such reassociations never require additional allocs 1214 or copies. 1215 1216 A reassociation is defined as a grouping of dimensions and is represented 1217 with an array of I64ArrayAttr attributes. 1218 1219 Example: 1220 1221 ```mlir 1222 %r = memref.expand_shape %0 [[0, 1], [2]] 1223 : memref<?x?xf32> into memref<?x5x?xf32> 1224 ``` 1225 1226 At most one dimension of a reassociation group (e.g., [0, 1] above) may be 1227 dynamic in the result type. Otherwise, the op would be ambiguous, as it 1228 would not be clear how the source dimension is extended. 1229 1230 If an op can be statically proven to be invalid (e.g, an expansion from 1231 `memref<10xf32>` to `memref<2x6xf32>`), it is rejected by the verifier. If 1232 it cannot statically be proven invalid (e.g., the full example above; it is 1233 unclear whether the first source dimension is divisible by 5), the op is 1234 accepted by the verifier. However, if the op is in fact invalid at runtime, 1235 the behavior is undefined. 1236 1237 The source memref can be zero-ranked. In that case, the reassociation 1238 indices must be empty and the result shape may only consist of unit 1239 dimensions. 1240 1241 For simplicity, this op may not be used to cast dynamicity of dimension 1242 sizes and/or strides. I.e., if and only if a source dimension is dynamic, 1243 there must be a dynamic result dimension in the corresponding reassociation 1244 group. Same for strides. 1245 1246 Note: This op currently assumes that the inner strides are of the 1247 source/result layout map are the faster-varying ones. 1248 }]; 1249 1250 let builders = [ 1251 // Builders using ReassociationIndices. 1252 OpBuilder<(ins "Type":$resultType, "Value":$src, 1253 "ArrayRef<ReassociationIndices>":$reassociation, 1254 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs), 1255 [{ 1256 build($_builder, $_state, resultType, src, attrs); 1257 $_state.addAttribute("reassociation", 1258 getReassociationIndicesAttribute($_builder, reassociation)); 1259 }]>, 1260 1261 // Builder using ReassociationExprs. 1262 OpBuilder<(ins "Type":$resultType, "Value":$src, 1263 "ArrayRef<ReassociationExprs>":$reassociation, 1264 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs), 1265 [{ 1266 auto reassociationMaps = 1267 convertReassociationMapsToIndices($_builder, reassociation); 1268 build($_builder, $_state, resultType, src, reassociationMaps, attrs); 1269 }]>, 1270 1271 // Builder that infers the result layout map. The result shape must be 1272 // specified. Otherwise, the op may be ambiguous. 1273 OpBuilder<(ins "ArrayRef<int64_t>":$resultShape, "Value":$src, 1274 "ArrayRef<ReassociationIndices>":$reassociation)> 1275 ]; 1276 let extraClassDeclaration = commonExtraClassDeclaration; 1277 let hasVerifier = 1; 1278} 1279 1280def MemRef_CollapseShapeOp : MemRef_ReassociativeReshapeOp<"collapse_shape"> { 1281 let summary = "operation to produce a memref with a smaller rank."; 1282 let description = [{ 1283 The `memref.collapse_shape` op produces a new view with a smaller rank 1284 whose sizes are a reassociation of the original `view`. The operation is 1285 limited to such reassociations, where subsequent, contiguous dimensions are 1286 collapsed into a single dimension. Such reassociations never require 1287 additional allocs or copies. 1288 1289 Collapsing non-contiguous dimensions is undefined behavior. When a group of 1290 dimensions can be statically proven to be non-contiguous, collapses of such 1291 groups are rejected in the verifier on a best-effort basis. In the general 1292 case, collapses of dynamically-sized dims with dynamic strides cannot be 1293 proven to be contiguous or non-contiguous due to limitations in the memref 1294 type. 1295 1296 A reassociation is defined as a continuous grouping of dimensions and is 1297 represented with an array of I64ArrayAttr attribute. 1298 1299 Note: Only the dimensions within a reassociation group must be contiguous. 1300 The remaining dimensions may be non-contiguous. 1301 1302 The result memref type can be zero-ranked if the source memref type is 1303 statically shaped with all dimensions being unit extent. In such a case, the 1304 reassociation indices must be empty. 1305 1306 Examples: 1307 1308 ```mlir 1309 // Dimension collapse (i, j) -> i' and k -> k' 1310 %1 = memref.collapse_shape %0 [[0, 1], [2]] : 1311 memref<?x?x?xf32, stride_spec> into memref<?x?xf32, stride_spec_2> 1312 ``` 1313 1314 For simplicity, this op may not be used to cast dynamicity of dimension 1315 sizes and/or strides. I.e., a result dimension must be dynamic if and only 1316 if at least one dimension in the corresponding reassociation group is 1317 dynamic. Similarly, the stride of a result dimension must be dynamic if and 1318 only if the corresponding start dimension in the source type is dynamic. 1319 1320 Note: This op currently assumes that the inner strides are of the 1321 source/result layout map are the faster-varying ones. 1322 }]; 1323 let builders = [ 1324 // Builders for a contracting reshape whose result type is computed from 1325 // `src` and `reassociation`. 1326 OpBuilder<(ins "Value":$src, 1327 "ArrayRef<ReassociationIndices>":$reassociation, 1328 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 1329 OpBuilder<(ins "Value":$src, 1330 "ArrayRef<ReassociationExprs>":$reassociation, 1331 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs), 1332 [{ 1333 auto reassociationMaps = 1334 convertReassociationMapsToIndices($_builder, reassociation); 1335 build($_builder, $_state, src, reassociationMaps, attrs); 1336 }]>, 1337 1338 // Builders for a reshape whose result type is passed explicitly. 1339 OpBuilder<(ins "Type":$resultType, "Value":$src, 1340 "ArrayRef<ReassociationIndices>":$reassociation, 1341 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs), 1342 [{ 1343 build($_builder, $_state, resultType, src, attrs); 1344 $_state.addAttribute("reassociation", 1345 getReassociationIndicesAttribute($_builder, reassociation)); 1346 }]>, 1347 OpBuilder<(ins "Type":$resultType, "Value":$src, 1348 "ArrayRef<ReassociationExprs>":$reassociation, 1349 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs), 1350 [{ 1351 auto reassociationMaps = 1352 convertReassociationMapsToIndices($_builder, reassociation); 1353 build($_builder, $_state, resultType, src, reassociationMaps, attrs); 1354 }]> 1355 ]; 1356 let extraClassDeclaration = commonExtraClassDeclaration # [{ 1357 /// Return `true` if this source MemRef type is guaranteed to be collapsible 1358 /// according to the given reassociation indices. In the presence of dynamic 1359 /// strides this is usually not the case. 1360 static bool isGuaranteedCollapsible( 1361 MemRefType srcType, ArrayRef<ReassociationIndices> reassociation); 1362 }]; 1363 1364 let hasVerifier = 1; 1365} 1366 1367//===----------------------------------------------------------------------===// 1368// StoreOp 1369//===----------------------------------------------------------------------===// 1370 1371def MemRef_StoreOp : MemRef_Op<"store", 1372 [TypesMatchWith<"type of 'value' matches element type of 'memref'", 1373 "memref", "value", 1374 "$_self.cast<MemRefType>().getElementType()">, 1375 MemRefsNormalizable]> { 1376 let summary = "store operation"; 1377 let description = [{ 1378 Store a value to a memref location given by indices. The value stored should 1379 have the same type as the elemental type of the memref. The number of 1380 arguments provided within brackets need to match the rank of the memref. 1381 1382 In an affine context, the indices of a store are restricted to SSA values 1383 bound to surrounding loop induction variables, 1384 [symbols](Affine.md/#restrictions-on-dimensions-and-symbols), results of a 1385 `constant` operation, or the result of an 1386 [`affine.apply`](Affine.md/#affineapply-affineapplyop) operation that can in 1387 turn take as arguments all of the aforementioned SSA values or the 1388 recursively result of such an `affine.apply` operation. 1389 1390 Example: 1391 1392 ```mlir 1393 memref.store %100, %A[%1, 1023] : memref<4x?xf32, #layout, memspace0> 1394 ``` 1395 1396 **Context:** The `load` and `store` operations are specifically crafted to 1397 fully resolve a reference to an element of a memref, and (in polyhedral 1398 `affine.if` and `affine.for` operations) the compiler can follow use-def 1399 chains (e.g. through [`affine.apply`](Affine.md/#affineapply-affineapplyop) 1400 operations) to precisely analyze references at compile-time using polyhedral 1401 techniques. This is possible because of the 1402 [restrictions on dimensions and symbols](Affine.md/#restrictions-on-dimensions-and-symbols) 1403 in these contexts. 1404 }]; 1405 1406 let arguments = (ins AnyType:$value, 1407 Arg<AnyMemRef, "the reference to store to", 1408 [MemWrite]>:$memref, 1409 Variadic<Index>:$indices); 1410 1411 let builders = [ 1412 OpBuilder<(ins "Value":$valueToStore, "Value":$memref), [{ 1413 $_state.addOperands(valueToStore); 1414 $_state.addOperands(memref); 1415 }]>]; 1416 1417 let extraClassDeclaration = [{ 1418 Value getValueToStore() { return getOperand(0); } 1419 1420 Value getMemRef() { return getOperand(1); } 1421 void setMemRef(Value value) { setOperand(1, value); } 1422 MemRefType getMemRefType() { 1423 return getMemRef().getType().cast<MemRefType>(); 1424 } 1425 }]; 1426 1427 let hasFolder = 1; 1428 let hasVerifier = 1; 1429 1430 let assemblyFormat = [{ 1431 $value `,` $memref `[` $indices `]` attr-dict `:` type($memref) 1432 }]; 1433} 1434 1435//===----------------------------------------------------------------------===// 1436// SubViewOp 1437//===----------------------------------------------------------------------===// 1438 1439def SubViewOp : MemRef_OpWithOffsetSizesAndStrides<"subview", [ 1440 DeclareOpInterfaceMethods<ViewLikeOpInterface>, NoSideEffect, 1441 AttrSizedOperandSegments, OffsetSizeAndStrideOpInterface 1442 ]> { 1443 let summary = "memref subview operation"; 1444 let description = [{ 1445 The "subview" operation converts a memref type to another memref type 1446 which represents a reduced-size view of the original memref as specified by 1447 the operation's offsets, sizes and strides arguments. 1448 1449 The SubView operation supports the following arguments: 1450 1451 * source: the "base" memref on which to create a "view" memref. 1452 * offsets: memref-rank number of offsets into the "base" memref at which to 1453 create the "view" memref. 1454 * sizes: memref-rank number of sizes which specify the sizes of the result 1455 "view" memref type. 1456 * strides: memref-rank number of strides that compose multiplicatively with 1457 the base memref strides in each dimension. 1458 1459 The representation based on offsets, sizes and strides support a 1460 partially-static specification via attributes specified through the 1461 `static_offsets`, `static_sizes` and `static_strides` arguments. A special 1462 sentinel value ShapedType::kDynamicSize and 1463 ShapedType::kDynamicStrideOrOffset encodes that the corresponding entry has 1464 a dynamic value. 1465 1466 A subview operation may additionally reduce the rank of the resulting view 1467 by removing dimensions that are statically known to be of size 1. 1468 1469 Example 1: 1470 1471 ```mlir 1472 %0 = memref.alloc() : memref<64x4xf32, affine_map<(d0, d1) -> (d0 * 4 + d1)>> 1473 1474 // Create a sub-view of "base" memref '%0' with offset arguments '%c0', 1475 // dynamic sizes for each dimension, and stride arguments '%c1'. 1476 %1 = memref.subview %0[%c0, %c0][%size0, %size1][%c1, %c1] 1477 : memref<64x4xf32, affine_map<(d0, d1) -> (d0 * 4 + d1)>> to 1478 memref<?x?xf32, affine_map<(d0, d1)[s0, s1] -> (d0 * s1 + d1 + s0)>> 1479 ``` 1480 1481 Example 2: 1482 1483 ```mlir 1484 %0 = memref.alloc() : memref<8x16x4xf32, affine_map<(d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)>> 1485 1486 // Create a sub-view of "base" memref '%0' with dynamic offsets, sizes, 1487 // and strides. 1488 // Note that dynamic offsets are represented by the linearized dynamic 1489 // offset symbol 's0' in the subview memref layout map, and that the 1490 // dynamic strides operands, after being applied to the base memref 1491 // strides in each dimension, are represented in the view memref layout 1492 // map as symbols 's1', 's2' and 's3'. 1493 %1 = memref.subview %0[%i, %j, %k][%size0, %size1, %size2][%x, %y, %z] 1494 : memref<8x16x4xf32, affine_map<(d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)>> to 1495 memref<?x?x?xf32, 1496 affine_map<(d0, d1, d2)[s0, s1, s2, s3] -> (d0 * s1 + d1 * s2 + d2 * s3 + s0)>> 1497 ``` 1498 1499 Example 3: 1500 1501 ```mlir 1502 %0 = memref.alloc() : memref<8x16x4xf32, affine_map<(d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)>> 1503 1504 // Subview with constant offsets, sizes and strides. 1505 %1 = memref.subview %0[0, 2, 0][4, 4, 4][1, 1, 1] 1506 : memref<8x16x4xf32, affine_map<(d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)>> to 1507 memref<4x4x4xf32, affine_map<(d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2 + 8)>> 1508 ``` 1509 1510 Example 4: 1511 1512 ```mlir 1513 %0 = memref.alloc(%arg0, %arg1) : memref<?x?xf32> 1514 1515 // Subview with constant size, but dynamic offsets and 1516 // strides. The resulting memref has a static shape, but if the 1517 // base memref has an affine map to describe the layout, the result 1518 // memref also uses an affine map to describe the layout. The 1519 // strides of the result memref is computed as follows: 1520 // 1521 // Let #map1 represents the layout of the base memref, and #map2 1522 // represents the layout of the result memref. A #mapsubview can be 1523 // constructed to map an index from the result memref to the base 1524 // memref (note that the description below uses more convenient 1525 // naming for symbols, while in affine maps, symbols are 1526 // represented as unsigned numbers that identify that symbol in the 1527 // given affine map. 1528 // 1529 // #mapsubview = (d0, d1)[o0, o1, t0, t1] -> (d0 * t0 + o0, d1 * t1 + o1) 1530 // 1531 // where, o0, o1, ... are offsets, and t0, t1, ... are strides. Then, 1532 // 1533 // #map2 = #map1.compose(#mapsubview) 1534 // 1535 // If the layout map is represented as 1536 // 1537 // #map1 = (d0, d1)[s0, s1, s2] -> (d0 * s1 + d1 * s2 + s0) 1538 // 1539 // then, 1540 // 1541 // #map2 = (d0, d1)[s0, s1, s2, o0, o1, t0, t1] -> 1542 // (d0 * s1 * t0 + d1 * s2 * t1 + o0 * s1 + o1 * s2 + s0) 1543 // 1544 // Representing this canonically 1545 // 1546 // #map2 = (d0, d1)[r0, r1, r2] -> (d0 * r1 + d1 * r2 + r0) 1547 // 1548 // where, r0 = o0 * s1 + o1 * s2 + s0, r1 = s1 * t0, r2 = s2 * t1. 1549 %1 = memref.subview %0[%i, %j][4, 4][%x, %y] : 1550 : memref<?x?xf32, affine_map<(d0, d1)[s0, s1, s2] -> (d0 * s1 + d1 * s2 + s0)>> to 1551 memref<4x4xf32, affine_map<(d0, d1)[r0, r1, r2] -> (d0 * r1 + d1 * r2 + r0)>> 1552 1553 // Note that the subview op does not guarantee that the result 1554 // memref is "inbounds" w.r.t to base memref. It is upto the client 1555 // to ensure that the subview is accessed in a manner that is 1556 // in-bounds. 1557 ``` 1558 1559 Example 5: 1560 1561 ```mlir 1562 // Rank-reducing subview. 1563 %1 = memref.subview %0[0, 0, 0][1, 16, 4][1, 1, 1] : 1564 memref<8x16x4xf32> to memref<16x4xf32> 1565 1566 // Original layout: 1567 // (d0, d1, d2) -> (64 * d0 + 16 * d1 + d2) 1568 // Subviewed layout: 1569 // (d0, d1, d2) -> (64 * (d0 + 3) + 4 * (d1 + 4) + d2 + 2) = (64 * d0 + 4 * d1 + d2 + 210) 1570 // After rank reducing: 1571 // (d0, d1) -> (4 * d0 + d1 + 210) 1572 %3 = memref.subview %2[3, 4, 2][1, 6, 3][1, 1, 1] : 1573 memref<8x16x4xf32> to memref<6x3xf32, offset: 210, strides: [4, 1]> 1574 ``` 1575 } 1576 }]; 1577 1578 let arguments = (ins AnyMemRef:$source, 1579 Variadic<Index>:$offsets, 1580 Variadic<Index>:$sizes, 1581 Variadic<Index>:$strides, 1582 I64ArrayAttr:$static_offsets, 1583 I64ArrayAttr:$static_sizes, 1584 I64ArrayAttr:$static_strides); 1585 let results = (outs AnyMemRef:$result); 1586 1587 let assemblyFormat = [{ 1588 $source `` 1589 custom<OperandsOrIntegersOffsetsOrStridesList>($offsets, $static_offsets) 1590 custom<OperandsOrIntegersSizesList>($sizes, $static_sizes) 1591 custom<OperandsOrIntegersOffsetsOrStridesList>($strides, $static_strides) 1592 attr-dict `:` type($source) `to` type($result) 1593 }]; 1594 1595 let builders = [ 1596 // Build a SubViewOp with mixed static and dynamic entries and custom 1597 // result type. If the type passed is nullptr, it is inferred. 1598 OpBuilder<(ins "Value":$source, "ArrayRef<OpFoldResult>":$offsets, 1599 "ArrayRef<OpFoldResult>":$sizes, "ArrayRef<OpFoldResult>":$strides, 1600 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 1601 // Build a SubViewOp with mixed static and dynamic entries and inferred 1602 // result type. 1603 OpBuilder<(ins "MemRefType":$resultType, "Value":$source, 1604 "ArrayRef<OpFoldResult>":$offsets, "ArrayRef<OpFoldResult>":$sizes, 1605 "ArrayRef<OpFoldResult>":$strides, 1606 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 1607 // Build a SubViewOp with static entries and custom result type. If the 1608 // type passed is nullptr, it is inferred. 1609 OpBuilder<(ins "Value":$source, "ArrayRef<int64_t>":$offsets, 1610 "ArrayRef<int64_t>":$sizes, "ArrayRef<int64_t>":$strides, 1611 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 1612 // Build a SubViewOp with static entries and inferred result type. 1613 OpBuilder<(ins "MemRefType":$resultType, "Value":$source, 1614 "ArrayRef<int64_t>":$offsets, "ArrayRef<int64_t>":$sizes, 1615 "ArrayRef<int64_t>":$strides, 1616 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 1617 // Build a SubViewOp with dynamic entries and custom result type. If the 1618 // type passed is nullptr, it is inferred. 1619 OpBuilder<(ins "Value":$source, "ValueRange":$offsets, 1620 "ValueRange":$sizes, "ValueRange":$strides, 1621 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>, 1622 // Build a SubViewOp with dynamic entries and inferred result type. 1623 OpBuilder<(ins "MemRefType":$resultType, "Value":$source, 1624 "ValueRange":$offsets, "ValueRange":$sizes, "ValueRange":$strides, 1625 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)> 1626 ]; 1627 1628 let extraClassDeclaration = extraBaseClassDeclaration # [{ 1629 /// Returns the type of the base memref operand. 1630 MemRefType getSourceType() { 1631 return getSource().getType().cast<MemRefType>(); 1632 } 1633 1634 /// The result of a subview is always a memref. 1635 MemRefType getType() { return getResult().getType().cast<MemRefType>(); } 1636 1637 /// A subview result type can be fully inferred from the source type and the 1638 /// static representation of offsets, sizes and strides. Special sentinels 1639 /// encode the dynamic case. 1640 static Type inferResultType(MemRefType sourceMemRefType, 1641 ArrayRef<int64_t> staticOffsets, 1642 ArrayRef<int64_t> staticSizes, 1643 ArrayRef<int64_t> staticStrides); 1644 static Type inferResultType(MemRefType sourceMemRefType, 1645 ArrayRef<OpFoldResult> staticOffsets, 1646 ArrayRef<OpFoldResult> staticSizes, 1647 ArrayRef<OpFoldResult> staticStrides); 1648 1649 /// A rank-reducing result type can be inferred from the desired result 1650 /// shape. Only the layout map is inferred. 1651 /// 1652 /// Note: The result shape cannot be inferred with just the result rank and 1653 /// and the desired sizes. In case there are more "ones" among the sizes 1654 /// than the difference in source/result rank, it is not clear which dims of 1655 /// size one should be dropped. 1656 static Type inferRankReducedResultType(ArrayRef<int64_t> resultShape, 1657 MemRefType sourceMemRefType, 1658 ArrayRef<int64_t> staticOffsets, 1659 ArrayRef<int64_t> staticSizes, 1660 ArrayRef<int64_t> staticStrides); 1661 static Type inferRankReducedResultType(ArrayRef<int64_t> resultShape, 1662 MemRefType sourceMemRefType, 1663 ArrayRef<OpFoldResult> staticOffsets, 1664 ArrayRef<OpFoldResult> staticSizes, 1665 ArrayRef<OpFoldResult> staticStrides); 1666 1667 /// Return the expected rank of each of the`static_offsets`, `static_sizes` 1668 /// and `static_strides` attributes. 1669 std::array<unsigned, 3> getArrayAttrMaxRanks() { 1670 unsigned rank = getSourceType().getRank(); 1671 return {rank, rank, rank}; 1672 } 1673 1674 /// Return the number of leading operands before the `offsets`, `sizes` and 1675 /// and `strides` operands. 1676 static unsigned getOffsetSizeAndStrideStartOperandIndex() { return 1; } 1677 1678 /// Return the dimensions of the source type that are dropped when 1679 /// the result is rank-reduced. 1680 llvm::SmallBitVector getDroppedDims(); 1681 }]; 1682 1683 let hasCanonicalizer = 1; 1684 let hasFolder = 1; 1685 let hasVerifier = 1; 1686} 1687 1688//===----------------------------------------------------------------------===// 1689// TensorStoreOp 1690//===----------------------------------------------------------------------===// 1691 1692def TensorStoreOp : MemRef_Op<"tensor_store", 1693 [SameOperandsShape, SameOperandsElementType, 1694 TypesMatchWith<"type of 'value' matches tensor equivalent of 'memref'", 1695 "memref", "tensor", 1696 "getTensorTypeFromMemRefType($_self)">]> { 1697 let summary = "tensor store operation"; 1698 let description = [{ 1699 Stores the contents of a tensor into a memref. The first operand is a value 1700 of tensor type, the second operand is a value of memref type. The shapes and 1701 element types of these must match, and are specified by the memref type. 1702 1703 Example: 1704 1705 ```mlir 1706 %9 = dim %8, 1 : tensor<4x?xf32> 1707 %10 = memref.alloc(%9) : memref<4x?xf32, #layout, memspace0> 1708 memref.tensor_store %8, %10 : memref<4x?xf32, #layout, memspace0> 1709 ``` 1710 }]; 1711 1712 let arguments = (ins AnyTensor:$tensor, Arg<AnyRankedOrUnrankedMemRef, 1713 "the reference to store to", [MemWrite]>:$memref); 1714 1715 let assemblyFormat = "$tensor `,` $memref attr-dict `:` type($memref)"; 1716} 1717 1718//===----------------------------------------------------------------------===// 1719// TransposeOp 1720//===----------------------------------------------------------------------===// 1721 1722def MemRef_TransposeOp : MemRef_Op<"transpose", [NoSideEffect]>, 1723 Arguments<(ins AnyStridedMemRef:$in, AffineMapAttr:$permutation)>, 1724 Results<(outs AnyStridedMemRef)> { 1725 let summary = "`transpose` produces a new strided memref (metadata-only)"; 1726 let description = [{ 1727 The `transpose` op produces a strided memref whose sizes and strides 1728 are a permutation of the original `in` memref. This is purely a metadata 1729 transformation. 1730 1731 Example: 1732 1733 ```mlir 1734 %1 = memref.transpose %0 (i, j) -> (j, i) : memref<?x?xf32> to memref<?x?xf32, affine_map<(d0, d1)[s0] -> (d1 * s0 + d0)>> 1735 ``` 1736 }]; 1737 1738 let builders = [ 1739 OpBuilder<(ins "Value":$in, "AffineMapAttr":$permutation, 1740 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>]; 1741 1742 let extraClassDeclaration = [{ 1743 static StringRef getPermutationAttrStrName() { return "permutation"; } 1744 ShapedType getShapedType() { return getIn().getType().cast<ShapedType>(); } 1745 }]; 1746 1747 let hasCustomAssemblyFormat = 1; 1748 let hasFolder = 1; 1749 let hasVerifier = 1; 1750} 1751 1752//===----------------------------------------------------------------------===// 1753// ViewOp 1754//===----------------------------------------------------------------------===// 1755 1756def MemRef_ViewOp : MemRef_Op<"view", [ 1757 DeclareOpInterfaceMethods<ViewLikeOpInterface>, NoSideEffect]> { 1758 let summary = "memref view operation"; 1759 let description = [{ 1760 The "view" operation extracts an N-D contiguous memref with empty layout map 1761 with arbitrary element type from a 1-D contiguous memref with empty layout 1762 map of i8 element type. The ViewOp supports the following arguments: 1763 1764 * A single dynamic byte-shift operand must be specified which represents a 1765 a shift of the base 1-D memref pointer from which to create the resulting 1766 contiguous memref view with identity layout. 1767 * A dynamic size operand that must be specified for each dynamic dimension 1768 in the resulting view memref type. 1769 1770 The "view" operation gives a structured indexing form to a flat 1-D buffer. 1771 Unlike "subview" it can perform a type change. The type change behavior 1772 requires the op to have special semantics because, e.g. a byte shift of 3 1773 cannot be represented as an offset on f64. 1774 For now, a "view" op: 1775 1776 1. Only takes a contiguous source memref with 0 offset and empty layout. 1777 2. Must specify a byte_shift operand (in the future, a special integer 1778 attribute may be added to support the folded case). 1779 3. Returns a contiguous memref with 0 offset and empty layout. 1780 1781 Example: 1782 1783 ```mlir 1784 // Allocate a flat 1D/i8 memref. 1785 %0 = memref.alloc() : memref<2048xi8> 1786 1787 // ViewOp with dynamic offset and static sizes. 1788 %1 = memref.view %0[%offset_1024][] : memref<2048xi8> to memref<64x4xf32> 1789 1790 // ViewOp with dynamic offset and two dynamic size. 1791 %2 = memref.view %0[%offset_1024][%size0, %size1] : 1792 memref<2048xi8> to memref<?x4x?xf32> 1793 ``` 1794 }]; 1795 1796 let arguments = (ins MemRefRankOf<[I8], [1]>:$source, 1797 Index:$byte_shift, 1798 Variadic<Index>:$sizes); 1799 let results = (outs AnyMemRef); 1800 1801 let extraClassDeclaration = [{ 1802 /// The result of a view is always a memref. 1803 MemRefType getType() { return getResult().getType().cast<MemRefType>(); } 1804 1805 /// Returns the dynamic sizes for this view operation. This is redundant 1806 /// with `sizes` but needed in template implementations. More specifically: 1807 /// ``` 1808 /// template <typename AnyMemRefDefOp> 1809 /// bool isMemRefSizeValidSymbol(AnyMemRefDefOp memrefDefOp, unsigned index, 1810 /// Region *region) 1811 /// ``` 1812 operand_range getDynamicSizes() { 1813 return {getSizes().begin(), getSizes().end()}; 1814 } 1815 }]; 1816 1817 let assemblyFormat = [{ 1818 $source `[` $byte_shift `]` `` `[` $sizes `]` attr-dict 1819 `:` type($source) `to` type(results) 1820 }]; 1821 1822 let hasCanonicalizer = 1; 1823 let hasVerifier = 1; 1824} 1825 1826//===----------------------------------------------------------------------===// 1827// AtomicRMWOp 1828//===----------------------------------------------------------------------===// 1829 1830def AtomicRMWOp : MemRef_Op<"atomic_rmw", [ 1831 AllTypesMatch<["value", "result"]>, 1832 TypesMatchWith<"value type matches element type of memref", 1833 "memref", "value", 1834 "$_self.cast<MemRefType>().getElementType()"> 1835 ]> { 1836 let summary = "atomic read-modify-write operation"; 1837 let description = [{ 1838 The `memref.atomic_rmw` operation provides a way to perform a read-modify-write 1839 sequence that is free from data races. The kind enumeration specifies the 1840 modification to perform. The value operand represents the new value to be 1841 applied during the modification. The memref operand represents the buffer 1842 that the read and write will be performed against, as accessed by the 1843 specified indices. The arity of the indices is the rank of the memref. The 1844 result represents the latest value that was stored. 1845 1846 Example: 1847 1848 ```mlir 1849 %x = memref.atomic_rmw "addf" %value, %I[%i] : (f32, memref<10xf32>) -> f32 1850 ``` 1851 }]; 1852 1853 let arguments = (ins 1854 AtomicRMWKindAttr:$kind, 1855 AnyTypeOf<[AnySignlessInteger, AnyFloat]>:$value, 1856 MemRefOf<[AnySignlessInteger, AnyFloat]>:$memref, 1857 Variadic<Index>:$indices); 1858 let results = (outs AnyTypeOf<[AnySignlessInteger, AnyFloat]>:$result); 1859 1860 let assemblyFormat = [{ 1861 $kind $value `,` $memref `[` $indices `]` attr-dict `:` `(` type($value) `,` 1862 type($memref) `)` `->` type($result) 1863 }]; 1864 1865 let extraClassDeclaration = [{ 1866 MemRefType getMemRefType() { 1867 return getMemref().getType().cast<MemRefType>(); 1868 } 1869 }]; 1870 let hasFolder = 1; 1871 let hasVerifier = 1; 1872} 1873 1874#endif // MEMREF_OPS 1875