'memref' Dialect
This dialect provides documentation for operations within the MemRef dialect.
Please post an RFC on the forum before adding or changing any operation in this dialect.
Operations ¶
The memref
dialect is intended to hold core memref creation and
manipulation ops, which are not strongly associated with any particular
other dialect or domain abstraction.
Operation definition ¶
memref.assume_alignment
(::mlir::memref::AssumeAlignmentOp) ¶
assertion that gives alignment information to the input memref
Syntax:
operation ::= `memref.assume_alignment` $memref `,` $alignment attr-dict `:` type($memref)
The assume_alignment
operation takes a memref and an integer of alignment
value, and internally annotates the buffer with the given alignment. If
the buffer isn’t aligned to the given alignment, the behavior is undefined.
This operation doesn’t affect the semantics of a correct program. It’s for optimization only, and the optimization is best-effort.
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
alignment | ::mlir::IntegerAttr | 32-bit signless integer attribute whose value is positive |
Operands: ¶
Operand | Description |
---|---|
memref | memref of any type values |
memref.clone
(::mlir::memref::CloneOp) ¶
Syntax:
operation ::= `memref.clone` $input attr-dict `:` type($input) `to` type($output)
Clones the data in the input view into an implicitly defined output view.
Usage:
%arg1 = memref.clone %arg0 : memref<?xf32> to memref<?xf32>
Note, that mutating the source or result of the clone operation leads to undefined behavior.
Operands: ¶
Operand | Description |
---|---|
input | memref of any type values |
Results: ¶
Result | Description |
---|---|
output | memref of any type values |
memref.dim
(::mlir::memref::DimOp) ¶
dimension index operation
Syntax:
operation ::= `memref.dim` attr-dict $memrefOrTensor `,` $index `:` type($memrefOrTensor)
The dim
operation takes a memref and a dimension operand of type index
.
It returns the size of the requested dimension of the given memref.
If the dimension index is out of bounds the behavior is undefined.
The specified memref type is that of the first operand.
Example:
// Always returns 4, can be constant folded:
%c0 = constant 0 : index
%x = memref.dim %A, %c0 : memref<4 x ? x f32>
// Returns the dynamic dimension of %A.
%c1 = constant 1 : index
%y = memref.dim %A, %c1 : memref<4 x ? x f32>
// Equivalent generic form:
%x = "memref.dim"(%A, %c0) : (memref<4 x ? x f32>, index) -> index
%y = "memref.dim"(%A, %c1) : (memref<4 x ? x f32>, index) -> index
Operands: ¶
Operand | Description |
---|---|
memrefOrTensor | any memref or tensor type |
index | index |
Results: ¶
Result | Description |
---|---|
result | index |
memref.load
(::mlir::memref::LoadOp) ¶
load operation
Syntax:
operation ::= `memref.load` $memref `[` $indices `]` attr-dict `:` type($memref)
The load
op reads an element from a memref specified by an index list. The
output of load is a new value with the same type as the elements of the
memref. The arity of indices is the rank of the memref (i.e., if the memref
loaded from is of rank 3, then 3 indices are required for the load following
the memref identifier).
In an affine.if
or affine.for
body, the indices of a load are restricted
to SSA values bound to surrounding loop induction variables,
symbols
, results of a
constant
operation
, or the result of an
affine.apply
operation that can in turn take as arguments all of the
aforementioned SSA values or the recursively result of such an
affine.apply
operation.
Example:
%1 = affine.apply affine_map<(d0, d1) -> (3*d0)> (%i, %j)
%2 = affine.apply affine_map<(d0, d1) -> (d1+1)> (%i, %j)
%12 = memref.load %A[%1, %2] : memref<8x?xi32, #layout, memspace0>
// Example of an indirect load (treated as non-affine)
%3 = affine.apply affine_map<(d0) -> (2*d0 + 1)>(%12)
%13 = memref.load %A[%3, %2] : memref<4x?xi32, #layout, memspace0>
Context: The load
and store
operations are specifically crafted to
fully resolve a reference to an element of a memref, and (in affine
affine.if
and affine.for
operations) the compiler can follow use-def
chains (e.g. through
affine.apply
operations) to precisely analyze references at compile-time using polyhedral
techniques. This is possible because of the
restrictions on dimensions and symbols
in these contexts.
Operands: ¶
Operand | Description |
---|---|
memref | memref of any type values |
indices | index |
Results: ¶
Result | Description |
---|---|
result | any type |
memref.alloc
(::mlir::memref::AllocOp) ¶
memory allocation operation
Syntax:
operation ::= `memref.alloc` `(`$dynamicSizes`)` (`` `[` $symbolOperands^ `]`)? attr-dict `:` type($memref)
The alloc
operation allocates a region of memory, as specified by its
memref type.
Example:
%0 = memref.alloc() : memref<8x64xf32, 1>
The optional list of dimension operands are bound to the dynamic dimensions specified in its memref type. In the example below, the ssa value ‘%d’ is bound to the second dimension of the memref (which is dynamic).
%0 = memref.alloc(%d) : memref<8x?xf32, 1>
The optional list of symbol operands are bound to the symbols of the memrefs affine map. In the example below, the ssa value ‘%s’ is bound to the symbol ‘s0’ in the affine map specified in the allocs memref type.
%0 = memref.alloc()[%s] : memref<8x64xf32,
affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1>
This operation returns a single ssa value of memref type, which can be used by subsequent load and store operations.
The optional alignment
attribute may be specified to ensure that the
region of memory that will be indexed is aligned at the specified byte
boundary.
%0 = memref.alloc()[%s] {alignment = 8} :
memref<8x64xf32, affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
alignment | ::mlir::IntegerAttr | 64-bit signless integer attribute whose minimum value is 0 |
Operands: ¶
Operand | Description |
---|---|
dynamicSizes | index |
symbolOperands | index |
Results: ¶
Result | Description |
---|---|
memref | memref of any type values |
memref.alloca
(::mlir::memref::AllocaOp) ¶
stack memory allocation operation
Syntax:
operation ::= `memref.alloca` `(`$dynamicSizes`)` (`` `[` $symbolOperands^ `]`)? attr-dict `:` type($memref)
The alloca
operation allocates memory on the stack, to be automatically
released when control transfers back from the region of its closest
surrounding operation with an
AutomaticAllocationScope
trait.
The amount of memory allocated is specified by its memref and additional
operands. For example:
%0 = memref.alloca() : memref<8x64xf32>
The optional list of dimension operands are bound to the dynamic dimensions specified in its memref type. In the example below, the SSA value ‘%d’ is bound to the second dimension of the memref (which is dynamic).
%0 = memref.alloca(%d) : memref<8x?xf32>
The optional list of symbol operands are bound to the symbols of the memref’s affine map. In the example below, the SSA value ‘%s’ is bound to the symbol ‘s0’ in the affine map specified in the allocs memref type.
%0 = memref.alloca()[%s] : memref<8x64xf32,
affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>>
This operation returns a single SSA value of memref type, which can be used by subsequent load and store operations. An optional alignment attribute, if specified, guarantees alignment at least to that boundary. If not specified, an alignment on any convenient boundary compatible with the type will be chosen.
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
alignment | ::mlir::IntegerAttr | 64-bit signless integer attribute whose minimum value is 0 |
Operands: ¶
Operand | Description |
---|---|
dynamicSizes | index |
symbolOperands | index |
Results: ¶
Result | Description |
---|---|
memref | memref of any type values |
memref.buffer_cast
(::mlir::memref::BufferCastOp) ¶
tensor to memref cast operation
Syntax:
operation ::= `memref.buffer_cast` $tensor attr-dict `:` type($memref)
Casts a tensor to a memref.
// Result type is tensor<4x?xf32>
%12 = memref.buffer_cast %10 : memref<4x?xf32, #map0, 42>
Note, that mutating the result of the buffer cast operation leads to undefined behavior.
Operands: ¶
Operand | Description |
---|---|
tensor | tensor of any type values |
Results: ¶
Result | Description |
---|---|
memref | unranked.memref of any type values or memref of any type values |
memref.cast
(::mlir::memref::CastOp) ¶
memref cast operation
Syntax:
operation ::= `memref.cast` $source attr-dict `:` type($source) `to` type($dest)
Syntax:
operation ::= ssa-id `=` `memref.cast` ssa-use `:` type `to` type
The memref.cast
operation converts a memref from one type to an equivalent
type with a compatible shape. The source and destination types are
compatible if:
a. Both are ranked memref types with the same element type, address space, and rank and:
- Both have the same layout or both have compatible strided layouts.
- The individual sizes (resp. offset and strides in the case of strided memrefs) may convert constant dimensions to dynamic dimensions and vice-versa.
If the cast converts any dimensions from an unknown to a known size, then it acts as an assertion that fails at runtime if the dynamic dimensions disagree with resultant destination size.
Example:
// Assert that the input dynamic shape matches the destination static shape.
%2 = memref.cast %1 : memref<?x?xf32> to memref<4x4xf32>
// Erase static shape information, replacing it with dynamic information.
%3 = memref.cast %1 : memref<4xf32> to memref<?xf32>
// The same holds true for offsets and strides.
// Assert that the input dynamic shape matches the destination static stride.
%4 = memref.cast %1 : memref<12x4xf32, offset:?, strides: [?, ?]> to
memref<12x4xf32, offset:5, strides: [4, 1]>
// Erase static offset and stride information, replacing it with
// dynamic information.
%5 = memref.cast %1 : memref<12x4xf32, offset:5, strides: [4, 1]> to
memref<12x4xf32, offset:?, strides: [?, ?]>
b. Either or both memref types are unranked with the same element type, and address space.
Example:
Cast to concrete shape.
%4 = memref.cast %1 : memref<*xf32> to memref<4x?xf32>
Erase rank information.
%5 = memref.cast %1 : memref<4x?xf32> to memref<*xf32>
Operands: ¶
Operand | Description |
---|---|
source | unranked.memref of any type values or memref of any type values |
Results: ¶
Result | Description |
---|---|
dest | unranked.memref of any type values or memref of any type values |
memref.dealloc
(::mlir::memref::DeallocOp) ¶
memory deallocation operation
Syntax:
operation ::= `memref.dealloc` $memref attr-dict `:` type($memref)
The dealloc
operation frees the region of memory referenced by a memref
which was originally created by the alloc
operation.
The dealloc
operation should not be called on memrefs which alias an
alloc’d memref (e.g. memrefs returned by view
operations).
Example:
%0 = memref.alloc() : memref<8x64xf32, (d0, d1) -> (d0, d1), 1>
memref.dealloc %0 : memref<8x64xf32, (d0, d1) -> (d0, d1), 1>
Operands: ¶
Operand | Description |
---|---|
memref | memref of any type values |
memref.get_global
(::mlir::memref::GetGlobalOp) ¶
get the memref pointing to a global variable
Syntax:
operation ::= `memref.get_global` $name `:` type($result) attr-dict
The memref.get_global
operation retrieves the memref pointing to a
named global variable. If the global variable is marked constant, writing
to the result memref (such as through a memref.store
operation) is
undefined.
Example:
%x = memref.get_global @foo : memref<2xf32>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
name | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
Results: ¶
Result | Description |
---|---|
result | statically shaped memref of any type values |
memref.global
(::mlir::memref::GlobalOp) ¶
declare or define a global memref variable
Syntax:
operation ::= `memref.global` ($sym_visibility^)?
(`constant` $constant^)?
$sym_name `:`
custom<GlobalMemrefOpTypeAndInitialValue>($type, $initial_value)
attr-dict
The memref.global
operation declares or defines a named global variable.
The backing memory for the variable is allocated statically and is described
by the type of the variable (which should be a statically shaped memref
type). The operation is a declaration if no inital_value
is specified,
else it is a definition. The initial_value
can either be a unit attribute
to represent a definition of an uninitialized global variable, or an
elements attribute to represent the definition of a global variable with an
initial value. The global variable can also be marked constant using the
constant
unit attribute. Writing to such constant global variables is
undefined.
The global variable can be accessed by using the memref.get_global
to
retrieve the memref for the global variable. Note that the memref
for such global variable itself is immutable (i.e., memref.get_global for a
given global variable will always return the same memref descriptor).
Example:
// Private variable with an initial value.
memref.global "private" @x : memref<2xf32> = dense<0.0,2.0>
// Declaration of an external variable.
memref.global "private" @y : memref<4xi32>
// Uninitialized externally visible variable.
memref.global @z : memref<3xf16> = uninitialized
// Externally visible constant variable.
memref.global constant @c : memref<2xi32> = dense<1, 4>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
sym_visibility | ::mlir::StringAttr | string attribute |
type | ::mlir::TypeAttr | any type attribute |
initial_value | ::mlir::Attribute | any attribute |
constant | ::mlir::UnitAttr | unit attribute |
memref.prefetch
(::mlir::memref::PrefetchOp) ¶
prefetch operation
The “prefetch” op prefetches data from a memref location described with subscript indices similar to memref.load, and with three attributes: a read/write specifier, a locality hint, and a cache type specifier as shown below:
memref.prefetch %0[%i, %j], read, locality<3>, data : memref<400x400xi32>
The read/write specifier is either ‘read’ or ‘write’, the locality hint ranges from locality<0> (no locality) to locality<3> (extremely local keep in cache). The cache type specifier is either ‘data’ or ‘instr’ and specifies whether the prefetch is performed on data cache or on instruction cache.
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
isWrite | ::mlir::BoolAttr | bool attribute |
localityHint | ::mlir::IntegerAttr | 32-bit signless integer attribute whose minimum value is 0 whose maximum value is 3 |
isDataCache | ::mlir::BoolAttr | bool attribute |
Operands: ¶
Operand | Description |
---|---|
memref | memref of any type values |
indices | index |
memref.reinterpret_cast
(::mlir::memref::ReinterpretCastOp) ¶
memref reinterpret cast operation
Syntax:
operation ::= `memref.reinterpret_cast` $source `to` `offset` `` `:`
custom<OperandsOrIntegersOffsetsOrStridesList>($offsets, $static_offsets)
`` `,` `sizes` `` `:`
custom<OperandsOrIntegersSizesList>($sizes, $static_sizes) `` `,` `strides`
`` `:`
custom<OperandsOrIntegersOffsetsOrStridesList>($strides, $static_strides)
attr-dict `:` type($source) `to` type($result)
Modify offset, sizes and strides of an unranked/ranked memref.
Example:
memref.reinterpret_cast %ranked to
offset: [0],
sizes: [%size0, 10],
strides: [1, %stride1]
: memref<?x?xf32> to memref<?x10xf32, offset: 0, strides: [1, ?]>
memref.reinterpret_cast %unranked to
offset: [%offset],
sizes: [%size0, %size1],
strides: [%stride0, %stride1]
: memref<*xf32> to memref<?x?xf32, offset: ?, strides: [?, ?]>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
static_offsets | ::mlir::ArrayAttr | 64-bit integer array attribute |
static_sizes | ::mlir::ArrayAttr | 64-bit integer array attribute |
static_strides | ::mlir::ArrayAttr | 64-bit integer array attribute |
Operands: ¶
Operand | Description |
---|---|
source | unranked.memref of any type values or memref of any type values |
offsets | index |
sizes | index |
strides | index |
Results: ¶
Result | Description |
---|---|
result | memref of any type values |
memref.reshape
(::mlir::memref::ReshapeOp) ¶
memref reshape operation
Syntax:
operation ::= `memref.reshape` $source `(` $shape `)` attr-dict `:` functional-type(operands, results)
The reshape
operation converts a memref from one type to an
equivalent type with a provided shape. The data is never copied or
modified. The source and destination types are compatible if both have the
same element type, same number of elements, address space and identity
layout map. The following combinations are possible:
a. Source type is ranked or unranked. Shape argument has static size. Result type is ranked.
// Reshape statically-shaped memref.
%dst = memref.reshape %src(%shape)
: (memref<4x1xf32>, memref<1xi32>) to memref<4xf32>
%dst0 = memref.reshape %src(%shape0)
: (memref<4x1xf32>, memref<2xi32>) to memref<2x2xf32>
// Flatten unranked memref.
%dst = memref.reshape %src(%shape)
: (memref<*xf32>, memref<1xi32>) to memref<?xf32>
b. Source type is ranked or unranked. Shape argument has dynamic size. Result type is unranked.
// Reshape dynamically-shaped 1D memref.
%dst = memref.reshape %src(%shape)
: (memref<?xf32>, memref<?xi32>) to memref<*xf32>
// Reshape unranked memref.
%dst = memref.reshape %src(%shape)
: (memref<*xf32>, memref<?xi32>) to memref<*xf32>
Operands: ¶
Operand | Description |
---|---|
source | unranked.memref of any type values or memref of any type values |
shape | 1D memref of signless integer or index values |
Results: ¶
Result | Description |
---|---|
result | unranked.memref of any type values or memref of any type values |
memref.store
(::mlir::memref::StoreOp) ¶
store operation
Syntax:
operation ::= `memref.store` $value `,` $memref `[` $indices `]` attr-dict `:` type($memref)
Store a value to a memref location given by indices. The value stored should have the same type as the elemental type of the memref. The number of arguments provided within brackets need to match the rank of the memref.
In an affine context, the indices of a store are restricted to SSA values
bound to surrounding loop induction variables,
symbols
, results of a
constant
operation
, or the result of an
affine.apply
operation that can in
turn take as arguments all of the aforementioned SSA values or the
recursively result of such an affine.apply
operation.
Example:
memref.store %100, %A[%1, 1023] : memref<4x?xf32, #layout, memspace0>
Context: The load
and store
operations are specifically crafted to
fully resolve a reference to an element of a memref, and (in polyhedral
affine.if
and affine.for
operations) the compiler can follow use-def
chains (e.g. through
affine.apply
operations) to precisely analyze references at compile-time using polyhedral
techniques. This is possible because of the
restrictions on dimensions and symbols
in these contexts.
Operands: ¶
Operand | Description |
---|---|
value | any type |
memref | memref of any type values |
indices | index |
memref.transpose
(::mlir::memref::TransposeOp) ¶
transpose
produces a new strided memref (metadata-only)
The transpose
op produces a strided memref whose sizes and strides
are a permutation of the original in
memref. This is purely a metadata
transformation.
Example:
%1 = memref.transpose %0 (i, j) -> (j, i) : memref<?x?xf32> to memref<?x?xf32, affine_map<(d0, d1)[s0] -> (d1 * s0 + d0)>>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
permutation | ::mlir::AffineMapAttr | AffineMap attribute |
Operands: ¶
Operand | Description |
---|---|
in | strided memref of any type values |
Results: ¶
Result | Description |
---|---|
«unnamed» | strided memref of any type values |
memref.view
(::mlir::memref::ViewOp) ¶
memref view operation
The “view” operation extracts an N-D contiguous memref with empty layout map with arbitrary element type from a 1-D contiguous memref with empty layout map of i8 element type. The ViewOp supports the following arguments:
- A single dynamic byte-shift operand must be specified which represents a a shift of the base 1-D memref pointer from which to create the resulting contiguous memref view with identity layout.
- A dynamic size operand that must be specified for each dynamic dimension in the resulting view memref type.
The “view” operation gives a structured indexing form to a flat 1-D buffer. Unlike “subview” it can perform a type change. The type change behavior requires the op to have special semantics because, e.g. a byte shift of 3 cannot be represented as an offset on f64. For now, a “view” op:
- Only takes a contiguous source memref with 0 offset and empty layout.
- Must specify a byte_shift operand (in the future, a special integer attribute may be added to support the folded case).
- Returns a contiguous memref with 0 offset and empty layout.
Example:
// Allocate a flat 1D/i8 memref.
%0 = memref.alloc() : memref<2048xi8>
// ViewOp with dynamic offset and static sizes.
%1 = memref.view %0[%offset_1024][] : memref<2048xi8> to memref<64x4xf32>
// ViewOp with dynamic offset and two dynamic size.
%2 = memref.view %0[%offset_1024][%size0, %size1] :
memref<2048xi8> to memref<?x4x?xf32>
Operands: ¶
Operand | Description |
---|---|
source | 1D memref of 8-bit signless integer values |
byte_shift | index |
sizes | index |
Results: ¶
Result | Description |
---|---|
«unnamed» | memref of any type values |
memref.subview
(::mlir::memref::SubViewOp) ¶
memref subview operation
Syntax:
operation ::= `memref.subview` $source ``
custom<OperandsOrIntegersOffsetsOrStridesList>($offsets, $static_offsets)
custom<OperandsOrIntegersSizesList>($sizes, $static_sizes)
custom<OperandsOrIntegersOffsetsOrStridesList>($strides, $static_strides)
attr-dict `:` type($source) `to` type($result)
The “subview” operation converts a memref type to another memref type which represents a reduced-size view of the original memref as specified by the operation’s offsets, sizes and strides arguments.
The SubView operation supports the following arguments:
- source: the “base” memref on which to create a “view” memref.
- offsets: memref-rank number of offsets into the “base” memref at which to create the “view” memref.
- sizes: memref-rank number of sizes which specify the sizes of the result “view” memref type.
- strides: memref-rank number of strides that compose multiplicatively with the base memref strides in each dimension.
The representation based on offsets, sizes and strides support a
partially-static specification via attributes specified through the
static_offsets
, static_sizes
and static_strides
arguments. A special
sentinel value ShapedType::kDynamicSize and
ShapedType::kDynamicStrideOrOffset encodes that the corresponding entry has
a dynamic value.
A subview operation may additionally reduce the rank of the resulting view by removing dimensions that are statically known to be of size 1.
Example 1:
%0 = memref.alloc() : memref<64x4xf32, (d0, d1) -> (d0 * 4 + d1)>
// Create a sub-view of "base" memref '%0' with offset arguments '%c0',
// dynamic sizes for each dimension, and stride arguments '%c1'.
%1 = memref.subview %0[%c0, %c0][%size0, %size1][%c1, %c1]
: memref<64x4xf32, (d0, d1) -> (d0 * 4 + d1) > to
memref<?x?xf32, (d0, d1)[s0, s1] -> (d0 * s1 + d1 + s0)>
Example 2:
%0 = memref.alloc() : memref<8x16x4xf32, (d0, d1, d1) -> (d0 * 64 + d1 * 4 + d2)>
// Create a sub-view of "base" memref '%0' with dynamic offsets, sizes,
// and strides.
// Note that dynamic offsets are represented by the linearized dynamic
// offset symbol 's0' in the subview memref layout map, and that the
// dynamic strides operands, after being applied to the base memref
// strides in each dimension, are represented in the view memref layout
// map as symbols 's1', 's2' and 's3'.
%1 = memref.subview %0[%i, %j, %k][%size0, %size1, %size2][%x, %y, %z]
: memref<8x16x4xf32, (d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)> to
memref<?x?x?xf32,
(d0, d1, d2)[s0, s1, s2, s3] -> (d0 * s1 + d1 * s2 + d2 * s3 + s0)>
Example 3:
%0 = memref.alloc() : memref<8x16x4xf32, (d0, d1, d1) -> (d0 * 64 + d1 * 4 + d2)>
// Subview with constant offsets, sizes and strides.
%1 = memref.subview %0[0, 2, 0][4, 4, 4][64, 4, 1]
: memref<8x16x4xf32, (d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)> to
memref<4x4x4xf32, (d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2 + 8)>
Example 4:
%0 = memref.alloc(%arg0, %arg1) : memref<?x?xf32>
// Subview with constant size, but dynamic offsets and
// strides. The resulting memref has a static shape, but if the
// base memref has an affine map to describe the layout, the result
// memref also uses an affine map to describe the layout. The
// strides of the result memref is computed as follows:
//
// Let #map1 represents the layout of the base memref, and #map2
// represents the layout of the result memref. A #mapsubview can be
// constructed to map an index from the result memref to the base
// memref (note that the description below uses more convenient
// naming for symbols, while in affine maps, symbols are
// represented as unsigned numbers that identify that symbol in the
// given affine map.
//
// #mapsubview = (d0, d1)[o0, o1, t0, t1] -> (d0 * t0 + o0, d1 * t1 + o1)
//
// where, o0, o1, ... are offsets, and t0, t1, ... are strides. Then,
//
// #map2 = #map1.compose(#mapsubview)
//
// If the layout map is represented as
//
// #map1 = (d0, d1)[s0, s1, s2] -> (d0 * s1 + d1 * s2 + s0)
//
// then,
//
// #map2 = (d0, d1)[s0, s1, s2, o0, o1, t0, t1] ->
// (d0 * s1 * t0 + d1 * s2 * t1 + o0 * s1 + o1 * s2 + s0)
//
// Representing this canonically
//
// #map2 = (d0, d1)[r0, r1, r2] -> (d0 * r1 + d1 * r2 + r0)
//
// where, r0 = o0 * s1 + o1 * s2 + s0, r1 = s1 * t0, r2 = s2 * t1.
%1 = memref.subview %0[%i, %j][4, 4][%x, %y] :
: memref<?x?xf32, (d0, d1)[s0, s1, s2] -> (d0 * s1 + d1 * s2 + s0)> to
memref<4x4xf32, (d0, d1)[r0, r1, r2] -> (d0 * r1 + d1 * r2 + r0)>
// Note that the subview op does not guarantee that the result
// memref is "inbounds" w.r.t to base memref. It is upto the client
// to ensure that the subview is accessed in a manner that is
// in-bounds.
Example 5:
// Rank-reducing subview.
%1 = memref.subview %0[0, 0, 0][1, 16, 4][1, 1, 1] :
memref<8x16x4xf32> to memref<16x4xf32>
%3 = memref.subview %2[3, 4, 2][1, 6, 3][1, 1, 1] :
memref<8x16x4xf32> to memref<6x3xf32, offset: 210, strides: [4, 1]>
}
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
static_offsets | ::mlir::ArrayAttr | 64-bit integer array attribute |
static_sizes | ::mlir::ArrayAttr | 64-bit integer array attribute |
static_strides | ::mlir::ArrayAttr | 64-bit integer array attribute |
Operands: ¶
Operand | Description |
---|---|
source | memref of any type values |
offsets | index |
sizes | index |
strides | index |
Results: ¶
Result | Description |
---|---|
result | memref of any type values |
memref.tensor_load
(::mlir::memref::TensorLoadOp) ¶
tensor load operation
Syntax:
operation ::= `memref.tensor_load` $memref attr-dict `:` type($memref)
Create a tensor from a memref, making an independent copy of the element data. The result value is a tensor whose shape and element type match the memref operand.
The opposite of this op is buffer_cast. Together, these two ops are useful for source/target materializations when doing type conversions involving tensors and memrefs.
Example:
// Produces a value of tensor<4x?xf32> type.
%12 = memref.tensor_load %10 : memref<4x?xf32, #layout, memspace0>
If tensor load is used in the bufferization steps, mutating the source buffer after loading leads to undefined behavior.
Operands: ¶
Operand | Description |
---|---|
memref | unranked.memref of any type values or memref of any type values |
Results: ¶
Result | Description |
---|---|
result | tensor of any type values |
memref.tensor_store
(::mlir::memref::TensorStoreOp) ¶
tensor store operation
Syntax:
operation ::= `memref.tensor_store` $tensor `,` $memref attr-dict `:` type($memref)
Stores the contents of a tensor into a memref. The first operand is a value of tensor type, the second operand is a value of memref type. The shapes and element types of these must match, and are specified by the memref type.
Example:
%9 = dim %8, 1 : tensor<4x?xf32>
%10 = alloc(%9) : memref<4x?xf32, #layout, memspace0>
memref.tensor_store %8, %10 : memref<4x?xf32, #layout, memspace0>
Operands: ¶
Operand | Description |
---|---|
tensor | tensor of any type values |
memref | unranked.memref of any type values or memref of any type values |
‘dma_start’ operation ¶
Syntax:
operation ::= `dma_start` ssa-use`[`ssa-use-list`]` `,`
ssa-use`[`ssa-use-list`]` `,` ssa-use `,`
ssa-use`[`ssa-use-list`]` (`,` ssa-use `,` ssa-use)?
`:` memref-type `,` memref-type `,` memref-type
Starts a non-blocking DMA operation that transfers data from a source memref to a destination memref. The operands include the source and destination memref’s each followed by its indices, size of the data transfer in terms of the number of elements (of the elemental type of the memref), a tag memref with its indices, and optionally two additional arguments corresponding to the stride (in terms of number of elements) and the number of elements to transfer per stride. The tag location is used by a dma_wait operation to check for completion. The indices of the source memref, destination memref, and the tag memref have the same restrictions as any load/store operation in an affine context (whenever DMA operations appear in an affine context). See restrictions on dimensions and symbols in affine contexts. This allows powerful static analysis and transformations in the presence of such DMAs including rescheduling, pipelining / overlap with computation, and checking for matching start/end operations. The source and destination memref need not be of the same dimensionality, but need to have the same elemental type.
For example, a dma_start
operation that transfers 32 vector elements from a
memref %src
at location [%i, %j]
to memref %dst
at [%k, %l]
would be
specified as shown below.
Example:
%size = constant 32 : index
%tag = alloc() : memref<1 x i32, affine_map<(d0) -> (d0)>, 4>
%idx = constant 0 : index
dma_start %src[%i, %j], %dst[%k, %l], %size, %tag[%idx] :
memref<40 x 8 x vector<16xf32>, affine_map<(d0, d1) -> (d0, d1)>, 0>,
memref<2 x 4 x vector<16xf32>, affine_map<(d0, d1) -> (d0, d1)>, 2>,
memref<1 x i32>, affine_map<(d0) -> (d0)>, 4>
‘dma_wait’ operation ¶
Syntax:
operation ::= `dma_wait` ssa-use`[`ssa-use-list`]` `,` ssa-use `:` memref-type
Blocks until the completion of a DMA operation associated with the tag element specified with a tag memref and its indices. The operands include the tag memref followed by its indices and the number of elements associated with the DMA being waited on. The indices of the tag memref have the same restrictions as load/store indices.
Example:
dma_wait %tag[%idx], %size : memref<1 x i32, affine_map<(d0) -> (d0)>, 4>