blob: c994e65fb58771ac24b39ac14c9c67d9e4722498 [file] [log] [blame]
// Copyright 2021 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef IREE_DIALECT_STREAM_OPS
#define IREE_DIALECT_STREAM_OPS
include "iree/compiler/Dialect/Stream/IR/StreamBase.td"
include "iree/compiler/Dialect/Stream/IR/StreamInterfaces.td"
include "iree/compiler/Dialect/Util/IR/UtilInterfaces.td"
include "mlir/Interfaces/FunctionInterfaces.td"
include "mlir/IR/BuiltinAttributeInterfaces.td"
include "mlir/IR/OpAsmInterface.td"
include "mlir/IR/SymbolInterfaces.td"
include "mlir/Interfaces/CallInterfaces.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/InferTypeOpInterface.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
include "mlir/Interfaces/ViewLikeInterface.td"
class Stream_PureOp<string mnemonic, list<Trait> traits = []> :
Stream_Op<mnemonic, !listconcat(traits, [Pure])>;
//===----------------------------------------------------------------------===//
// Execution context ops
//===----------------------------------------------------------------------===//
def OpGroupContextOps : OpDocGroup {
let summary = "Execution context ops";
let description = [{
Operations for interacting with the execution context that stream operations
execute within.
}];
}
let opDocGroup = OpGroupContextOps in {
def Stream_ContextResolveOp : Stream_PureOp<"context.resolve", [
Stream_AffinityOp,
]> {
let summary = [{resolves low-level context resources based on type}];
let description = [{
WIP; allows for accessing the implementation details of lower-level dialects
such as the HAL. This will likely be reworked in the future to either
live inside other dialects, use some op interface instead of having a
dedicated op here, or remove the op entirely and make resolution happen
explicitly.
Examples:
```
// Returns a HAL device.
= stream.context.resolve on(#something) : !hal.device
// Returns a HAL device and (optional) queue affinity.
= stream.context.resolve on(#something) : !hal.device, i64
// Returns a HAL allocator and (optional) queue affinity.
= stream.context.resolve on(#something) : !hal.allocator, i64
```
}];
let arguments = (ins
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Variadic<AnyType>:$results
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
attr-dict `:` type($results)
}];
}
} // OpGroupContextOps
//===----------------------------------------------------------------------===//
// Generic resource ops
//===----------------------------------------------------------------------===//
def OpGroupResourceOps : OpDocGroup {
let summary = "Resource ops";
let description = "Generic resource ops.";
}
let opDocGroup = OpGroupResourceOps in {
def Stream_ResourceAllocOp : Stream_Op<"resource.alloc", [
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Util_SizeAwareOp,
AlwaysSpeculatable,
MemoryEffects<[MemAlloc]>,
]> {
let summary = [{allocates a persistent resource}];
let description = [{
Allocates a persistent value (one that is long-lived and possibly external
to the program) with undefined contents. Consumers of the allocated
result must assume nothing of the contents and use `discard` access.
Uninitialized allocations will have undefined contents and must only be used
when all bytes are discarded prior to any reads. Runtimes decide what
"undefined contents" means and here it only indicates that execution will be
correct even if the memory starts with non-zero values.
If multiple values are allocated from the same operation it implies that
they have matching lifetimes. When lowering to execution environments the
separate allocations may be fused into one or more slab allocations in order
to reduce overheads. How many allocations can be fused is based on the size
of the individual resources and the target constraints (how large any single
buffer may be, etc).
}];
let arguments = (ins
Stream_Size:$storage_size,
UnitAttr:$uninitialized,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`uninitialized` $uninitialized^)?
attr-dict `:`
type($result) `{` $storage_size `}`
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getStorageSize(); }
// Creates a single shared allocation for multiple suballocations.
// Suballocations are defined by entries in the struct-of-arrays-style
// `{locs, storageSizes}` set. Currently all result types must match.
// Returns the allocation and subviews into all suballocated resources.
static std::pair<IREE::Stream::ResourceAllocOp, SmallVector<Value>>
createSuballocations(
Type resourceType,
ArrayRef<Location> locs, ValueRange storageSizes,
bool uninitialized, AffinityAttr affinityAttr, OpBuilder &builder);
}];
let hasCanonicalizer = 1;
}
def Stream_ResourceAllocaOp : Stream_Op<"resource.alloca", [
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Stream_TimelineOp,
Util_SizeAwareOp,
AlwaysSpeculatable,
MemoryEffects<[MemAlloc]>,
]> {
let summary = [{allocates a transient value with undefined contents}];
let description = [{
Allocates a transient value (one that is short-lived and local to the
current computation) with undefined contents. Consumers of the allocated
result must assume nothing of the contents and use `discard` access.
The resource returned is not valid for use until the timepoint is reached;
execution using this resource must await on the timepoint.
}];
let arguments = (ins
Stream_Size:$storage_size,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyResource:$result,
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
`uninitialized`
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`):(`:`)?
attr-dict
type($result) `{` $storage_size `}`
`=` `` `>`
type($result_timepoint)
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getStorageSize(); }
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
// Creates a single shared allocation for multiple suballocations.
// Suballocations are defined by entries in the struct-of-arrays-style
// `{locs, storageSizes}` set. Currently all result types must match.
// Returns the allocation and subviews into all suballocated resources.
static std::pair<IREE::Stream::ResourceAllocaOp, SmallVector<Value>>
createSuballocations(
Type timepointType, Type resourceType,
ArrayRef<Location> locs, ValueRange storageSizes,
Value awaitTimepoint, AffinityAttr affinityAttr, OpBuilder &builder);
}];
let hasCanonicalizer = 1;
}
def Stream_ResourceDeallocaOp : Stream_Op<"resource.dealloca", [
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Stream_TimelineOp,
Util_SizeAwareOp,
MemoryEffects<[MemFree]>,
]> {
let summary = [{frees a transient value when available}];
let description = [{
Deallocates a transient value (one that is short-lived and local to the
current computation) previously allocated using `stream.resource.alloca`.
The resource is considered live and valid until the provided timepoint is
reached and the memory is only made available for future requests after
the result timepoint is reached.
}];
let arguments = (ins
Stream_AnyResource:$operand,
Stream_Size:$operand_size,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`)?
$operand `:` type($operand) `{` $operand_size `}`
`=` `` `>` type($result_timepoint)
attr-dict
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getOperandSize(); }
Value getResultSize(unsigned idx) { return {}; }
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
}];
let hasCanonicalizer = 1;
}
def Stream_ResourceSizeOp : Stream_PureOp<"resource.size", [
Util_SizeAwareOp,
]> {
let summary = [{returns the size of the resource storage in bytes}];
let description = [{
Returns a possibly runtime-dynamic byte size of the resource backing
storage. This may differ from the logical storage size of a value based on
the alignment requirements of the target as well as encoding of higher level
values such as sparse tensor formats.
}];
let arguments = (ins
Stream_AnyResource:$operand,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Size:$result
);
let assemblyFormat = [{
$operand
attr-dict `:` type($operand)
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getResult(); }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_ResourceTryMapOp : Stream_PureOp<"resource.try_map", [
Stream_AffinityOp,
Util_SizeAwareOp,
MemoryEffects<[MemAlloc]>,
]> {
let summary = [{maps read-only memory into a resource}];
let description = [{
Synchronously maps a host heap buffer into a stream-accessible resource
with the requested lifetime. If the given source cannot be mapped the
`did_map` result will be 0 and users must find another route into memory
(such as file I/O). The resulting resource is not coherent with the source
and behavior is undefined if the underlying contents change.
}];
let arguments = (ins
Util_BufferType:$source,
Stream_Offset:$source_offset,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
I1:$did_map,
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$source `[` $source_offset `]` `:`
type($source)
`->`
type($did_map) `,` type($result) `` `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_ResourceLoadOp : Stream_Op<"resource.load", [
Util_SizeAwareOp,
]> {
let summary = [{loads a value from a staging resource}];
let description = [{
Returns the element(s) at the given offset in the staging resource.
The operation will complete synchronously against the resource though it may
introduce a yield point if the staging resource needs to be transferred.
}];
let arguments = (ins
Stream_StagingResource:$source,
Stream_Size:$source_size,
Stream_Offset:$source_offset
);
let results = (outs
AnyTypeOf<[Stream_PrimitiveType, AnyVector]>:$result
);
let assemblyFormat = [{
$source `[` $source_offset `]` `:`
type($source) `` `{` $source_size `}`
`->`
type($result)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_ResourceStoreOp : Stream_Op<"resource.store", [
Util_SizeAwareOp,
MemoryEffects<[MemWrite]>,
]> {
let summary = [{stores a value into a staging resource}];
let description = [{
The operation will complete synchronously against the resource though it may
introduce a yield point if the staging resource needs to be acquired.
}];
let arguments = (ins
Stream_StagingResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
AnyTypeOf<[Stream_PrimitiveType, AnyVector]>:$value
);
let assemblyFormat = [{
$value `,`
$target `[` $target_offset `]` `:`
type($value)
`->`
type($target) `{` $target_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_ResourcePackOp : Stream_PureOp<"resource.pack", [
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
Stream_AffinityOp,
]> {
let summary = [{packs variable-sized slices into a single slab}];
let description = [{
Performs a greedy packing of one or more sized slices with specified
lifetimes and returns their relative offsets in an aliased linear space.
Slices are `[start, end] = %slice_byte_size`, where the start and end values
define an inclusive lifetime range and the size is the total number of bytes
required to be live for that range.
```mlir
// Computes the total length required for the packed values and the offsets
// of the 3 slices requested relative to the base of the packed memory:
%total_length, %offset_0, %offset_1, %offset_2 =
stream.resource.pack
// Each slice gets one result offset:
slices({
// 3 slices where A and B overlap and will get unique offsets
// while B and C do not overlap and are allowed to alias.
[0, 10] = %size_0, // A => %offset_0
[3, 8] = %size_1, // B => %offset_1
[9, 10] = %size_2, // C => %offset_2
...
}) : index
```
The lifetime start and end points (inclusive) are only used for relative
comparisons and may originate with any meaning (op order in block, epoch,
phase of the moon, etc). The packing algorithm uses the intervals to
determine slice liveness and when aliasing is safe.
The size of each slice may either be a constant or runtime-computed dynamic
value. Constant slices can achieve more dense packing than the dynamic
values and CSE/canonicalization should be applied to ensure that as many of
the dynamic values are equivalent if possible.
The total length required to pack all slices is returned and can be used to
acquire storage. The individual slice offsets are 0-based and as such if are
directly used as buffer offsets may need additional offsetting. This can
either be applied via the optional `offset` operand or slicing of the
underlying allocation buffer.
}];
let arguments = (ins
Optional<Stream_Offset>:$offset,
Stream_IndexArrayAttr:$lifetime_intervals,
Variadic<Stream_Size>:$dynamic_slice_sizes,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Size:$total_length,
Variadic<Stream_Offset>:$packed_offsets
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`offset` `(` $offset^ `)`)?
`slices` `(` `{`
custom<PackSliceRanges>($lifetime_intervals,
$dynamic_slice_sizes,
type($packed_offsets))
`}` `)`
`:` type($total_length)
attr-dict-with-keyword
}];
let hasVerifier = 1;
let extraClassDeclaration = [{
struct Slice {
int64_t lifetimeStart;
int64_t lifetimeEnd;
Value dynamicSize;
Value packedOffset;
bool operator==(const Slice &rhs) const {
return lifetimeStart == rhs.lifetimeStart &&
lifetimeEnd == rhs.lifetimeEnd;
}
bool operator!=(const Slice &rhs) const {
return !(*this == rhs);
}
bool operator<(const Slice &rhs) const {
return std::make_pair(lifetimeStart, lifetimeEnd) <
std::make_pair(rhs.lifetimeStart, rhs.lifetimeEnd);
}
bool intersects(const Slice &rhs) const {
return lifetimeEnd >= rhs.lifetimeStart &&
rhs.lifetimeEnd >= lifetimeStart;
}
};
/// Returns all of the slices to be packed.
/// Order is ascending by lifetime interval (post-canonicalization).
SmallVector<Slice> getSlices();
}];
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_ResourceConstantsOp : Stream_PureOp<"resource.constants", [
SameVariadicResultSize,
Stream_AffinityOp,
Stream_TimelineOp,
Util_SizeAwareOp,
]> {
let summary = [{asynchronously uploads or maps constant values}];
let description = [{
Represents an upload of constant resources that may be packed, suballocated,
and mapped depending on the final lowering target.
In runtime environments where memory is shared between host and device this
turns into a mapping operation that avoids additional memory allocation and
copies. When memory cannot be shared an asynchronous stream will be created
to allocate and copy all of the constant values.
Though this op returns a unique resource for each constant value it's
expected that almost all end up aliasing into the same storage. The exact
packing and number of storage resources that are needed are not known until
lowering to a particular backend, though, so they are separate here for
proper usage tracking.
Both constant and variable resources can be produced; a constant is
immutable while a variable will be treated as a constant-value initializer
for a mutable resource. By modeling these together it's not required that
variable initializers first be allocated, copied to the target, and then
copied into the variable storage if the target is capable of doing a direct
upload or mapping.
}];
let arguments = (ins
TypedArrayAttrBase<AnyAttr, "constant value array attribute">:$values,
Variadic<Stream_Size>:$result_sizes,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Variadic<AnyTypeOf<[
Stream_ConstantResource,
Stream_VariableResource,
]>>:$results,
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
attr-dict `:`
custom<ConstantValueList>(type($results),
$result_sizes,
$values)
`\n` ` ` ` ` `=` `` `>` type($result_timepoint)
}];
let hasVerifier = 1;
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getResultSizes()[idx]; }
SmallVector<Value> getAwaitTimepoints() { return {}; }
}];
}
def Stream_ResourceSubviewOp : Stream_PureOp<"resource.subview", [
AllTypesMatch<["source", "result"]>,
DeclareOpInterfaceMethods<ViewLikeOpInterface>,
DeclareOpInterfaceMethods<Stream_StreamableOp, [
"isMetadata",
]>,
Util_SizeAwareOp,
Util_SubrangeOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{slices out a cloned subview of a value}];
let description = [{
Aliases a byte subrange of a resource.
}];
let arguments = (ins
Stream_AnyResource:$source,
Stream_Size:$source_size,
Stream_Offset:$source_offset,
Stream_Size:$result_size
);
let results = (outs
Stream_AnyResource:$result
);
let assemblyFormat = [{
$source `[` $source_offset `]` `:`
type($source) `` `{` $source_size `}` `->`
type($result) `` `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return getResultSize(); }
Value getSubrangeResource() { return getSource(); }
Value getSubrangeResourceSize() { return getSourceSize(); }
Value getSubrangeOffset() { return getSourceOffset(); }
Value getSubrangeLength() { return getResultSize(); }
Value getSubrangeResult() { return getResult(); }
// Walks up the use-def chain to find a subview op that feeds into |value|.
// Stops at any timeline op that may indicate a change in resource contents.
static IREE::Stream::ResourceSubviewOp findSubviewOp(Value value);
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
} // OpGroupResourceOps
//===----------------------------------------------------------------------===//
// Parameter I/O ops
//===----------------------------------------------------------------------===//
def OpGroupParameterOps : OpDocGroup {
let summary = "Resource parameter I/O ops";
let description = "Resource parameter I/O ops.";
}
let opDocGroup = OpGroupParameterOps in {
def Stream_ParameterLoadOp : Stream_PureOp<"parameter.load", [
AttrSizedOperandSegments,
AllTypesMatch<["results"]>,
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Stream_CmdPhaseOp,
Stream_TimelineOp,
Util_SizeAwareOp,
]> {
let summary = [{reads one or more resources from a parameter scope}];
let description = [{
Asynchronously reads one or more resources from an external parameter
provider and returns the resulting stream resources. Depending on the
resource type this may alias existing cached storage or be directly mapped
to the parameter origin or result in a copy as if `stream.resource.alloca`
and `stream.parameter.read` had been used per parameter.
}];
let arguments = (ins
OptionalAttr<StrAttr>:$source_scope,
StrArrayAttr:$source_keys,
Variadic<I64>:$source_offsets,
Variadic<Stream_Size>:$result_sizes,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Variadic<Stream_AnyStreamResource>:$results,
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`)?
`{`
custom<ParameterLoadOperations>(
$source_scope, $source_keys, $source_offsets,
type($results), $result_sizes)
`}`
`=` `` `>`
type($result_timepoint)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getResultSizes()[idx]; }
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_ParameterReadOp : Stream_Op<"parameter.read", [
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Stream_CmdPhaseOp,
Stream_TimelineOp,
Util_SizeAwareOp,
]> {
let summary = [{reads a resource from a parameter scope}];
let description = [{
Asynchronously reads a resource from an external parameter provider into the
provided target resource range.
}];
let arguments = (ins
OptionalAttr<StrAttr>:$source_scope,
StrAttr:$source_key,
I64:$source_offset,
Stream_AnyStreamResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Size:$target_length,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`)?
custom<ParameterReference>($source_scope, $source_key)
`` `[` $source_offset `]` `->`
$target `[` $target_offset `for` $target_length `]` `:`
type($target) `` `{` $target_size `}`
`=` `` `>`
type($result_timepoint)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return {}; }
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_ParameterWriteOp : Stream_Op<"parameter.write", [
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Stream_CmdPhaseOp,
Stream_TimelineOp,
Util_SizeAwareOp,
]> {
let summary = [{writes a resource to a parameter scope}];
let description = [{
Asynchronously writes a resource to an external parameter provider from
the provided source resource range.
}];
let arguments = (ins
Stream_AnyStreamResource:$source,
Stream_Size:$source_size,
Stream_Offset:$source_offset,
Stream_Size:$source_length,
OptionalAttr<StrAttr>:$target_scope,
StrAttr:$target_key,
I64:$target_offset,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`)?
$source `[` $source_offset `for` $source_length `]` `:`
type($source) `` `{` $source_size `}` `->`
custom<ParameterReference>($target_scope, $target_key)
`` `[` $target_offset `]`
`=` `` `>`
type($result_timepoint)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return {}; }
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_ParameterGatherOp : Stream_Op<"parameter.gather", [
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Stream_CmdPhaseOp,
Stream_TimelineOp,
Util_SizeAwareOp,
]> {
let summary = [{gathers multiple resources from a parameter scope}];
let description = [{
Asynchronously gathers one or more resources into a single target stream
resource. This is equivalent to one `stream.parameter.read` per parameter
but allows implementations that can batch operations to do so without
additional timeline overhead.
}];
let arguments = (ins
OptionalAttr<StrAttr>:$source_scope,
StrArrayAttr:$source_keys,
Variadic<I64>:$source_offsets,
Stream_AnyStreamResource:$target,
Stream_Size:$target_size,
Variadic<Stream_Offset>:$target_offsets,
Variadic<Stream_Size>:$target_lengths,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`)?
`{`
custom<ParameterGatherOperations>(
$source_scope, $source_keys, $source_offsets,
$target, type($target), $target_size, $target_offsets, $target_lengths)
`}`
`=` `` `>`
type($result_timepoint)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return {}; }
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_ParameterScatterOp : Stream_Op<"parameter.scatter", [
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Stream_CmdPhaseOp,
Stream_TimelineOp,
Util_SizeAwareOp,
]> {
let summary = [{scatters multiple resources to a parameter scope}];
let description = [{
Asynchronously scatters one or more resources from a single source resource
into one or more parameters. This is equivalent to one
`stream.parameter.write` per parameter but allows implementations that can
batch operations to do so without additional overhead.
}];
let arguments = (ins
Stream_AnyStreamResource:$source,
Stream_Size:$source_size,
Variadic<Stream_Offset>:$source_offsets,
Variadic<Stream_Size>:$source_lengths,
OptionalAttr<StrAttr>:$target_scope,
StrArrayAttr:$target_keys,
Variadic<I64>:$target_offsets,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`)?
`{`
custom<ParameterScatterOperations>(
$source, type($source), $source_size, $source_offsets, $source_lengths,
$target_scope, $target_keys, $target_offsets)
`}`
`=` `` `>`
type($result_timepoint)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return {}; }
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
} // OpGroupParameterOps
//===----------------------------------------------------------------------===//
// File ops
//===----------------------------------------------------------------------===//
def OpGroupFileOps : OpDocGroup {
let summary = "File ops";
let description = "File ops.";
}
let opDocGroup = OpGroupFileOps in {
def Stream_FileConstantOp : Stream_PureOp<"file.constant", [
Stream_AffinityOp,
Util_SizeAwareOp,
MemoryEffects<[MemAlloc]>,
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
DeclareOpInterfaceMethods<Util_SubrangeOperandOpInterface>,
]> {
let summary = [{creates a file backed by the provided constant host memory}];
let description = [{
Synchronously wraps a host heap buffer into a stream-accessible file handle.
Changing the source buffer after definition has undefined behavior.
}];
let arguments = (ins
Util_BufferType:$source,
Util_Size:$source_size,
Stream_Offset:$source_offset,
Stream_Size:$source_length,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_File:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$source `[` $source_offset `for` $source_length `]` `:`
type($source) `` `{` $source_size `}`
`->`
type($result)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return {}; }
}];
}
def Stream_FileReadOp : Stream_Op<"file.read", [
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Stream_CmdPhaseOp,
Stream_TimelineOp,
Util_SizeAwareOp,
]> {
let summary = [{reads a segment of a file into a resource}];
let description = [{
Asynchronously reads a segment of a file into a resource.
Some implementations can stream directly from the source file into
device-local memory and file ops should be preferred to manually staging
memory through host buffers.
}];
let arguments = (ins
Stream_File:$source,
I64:$source_offset,
Stream_AnyStreamResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Size:$length,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`):(`:`)?
$source `[` $source_offset `]` `,`
$target `[` $target_offset `]` `,`
$length `:`
type($source) `->`
type($target) `` `{` $target_size `}`
`=` `` `>`
type($result_timepoint)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return {}; }
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_FileWriteOp : Stream_Op<"file.write", [
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Stream_CmdPhaseOp,
Stream_TimelineOp,
Util_SizeAwareOp,
]> {
let summary = [{writes a segment of a file from a resource}];
let description = [{
Asynchronously writes a segment of a resource into a file.
The file range must be valid within the file as this operation cannot
grow the underlying file storage.
Some implementations can stream directly from device-local memory into the
target file and file ops should be preferred to manually staging memory
through host buffers.
}];
let arguments = (ins
Stream_AnyStreamResource:$source,
Stream_Size:$source_size,
Stream_Offset:$source_offset,
Stream_File:$target,
I64:$target_offset,
Stream_Size:$length,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`):(`:`)?
$source `[` $source_offset `]` `,`
$target `[` $target_offset `]` `,`
$length `:`
type($source) `` `{` $source_size `}` `->`
type($target)
`=` `` `>`
type($result_timepoint)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return {}; }
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
} // OpGroupFileOps
//===----------------------------------------------------------------------===//
// Pseudo ops for conversion support
//===----------------------------------------------------------------------===//
def OpGroupPseudoOps : OpDocGroup {
let summary = "Pseudo Ops";
let description = "Pseudo ops for conversion support.";
}
let opDocGroup = OpGroupPseudoOps in {
def Stream_TensorImportOp : Stream_PureOp<"tensor.import", [
Stream_AffinityOp,
Util_ShapeAwareOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{conversion placeholder for other->stream type conversion}];
let description = [{
Defines a conversion from a higher-level dialect type such as `tensor` that
is resolved during lowering into the stream dialect. This can be used to
interoperate between levels of the stack that require specifying stream
types and those that prior to lowering do not handle them.
}];
let arguments = (ins
AnyType:$source,
TypeAttr:$result_encoding,
Stream_ShapeDynamicDims:$result_encoding_dims,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
AnyTypeOf<[Stream_AnyStreamResource, Stream_StagingResource]>:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$source `:`
type($source)
`->`
$result_encoding (`` `{` $result_encoding_dims^ `}`)?
`in`
type($result) `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return ValueRange{}; }
ValueRange getResultDynamicDims(unsigned idx) { return getResultEncodingDims(); }
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_TensorExportOp : Stream_PureOp<"tensor.export", [
Stream_AffinityOp,
Util_ShapeAwareOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{conversion placeholder for stream->other type conversion}];
let description = [{
Defines a conversion to a higher-level dialect type such as `tensor` that
is resolved during lowering into the stream dialect. This can be used to
interoperate between levels of the stack that require specifying stream
types and those that prior to lowering do not handle them.
}];
let arguments = (ins
AnyTypeOf<[Stream_AnyStreamResource, Stream_StagingResource]>:$source,
TypeAttr:$source_encoding,
Stream_ShapeDynamicDims:$source_encoding_dims,
Stream_Size:$source_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
AnyType:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$source `:`
$source_encoding (`` `{` $source_encoding_dims^ `}`)?
`in`
type($source) `` `{` $source_size `}`
`->`
type($result)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return getSourceEncodingDims(); }
ValueRange getResultDynamicDims(unsigned idx) { return ValueRange{}; }
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
} // OpGroupPseudoOps
//===----------------------------------------------------------------------===//
// High-level tensor ops
//===----------------------------------------------------------------------===//
def OpGroupTensorOps : OpDocGroup {
let summary = "Tensor ops";
let description = "";
}
let opDocGroup = OpGroupTensorOps in {
def Stream_TensorSizeOfOp : Stream_PureOp<"tensor.sizeof", [
Stream_AffinityOp,
Stream_TensorPhaseOp,
]> {
let summary = [{calculates the storage size of a given high-level type}];
let description = [{
Target-dependent storage size calculation using a high-level annotated type.
While within the stream dialect the storage size of a value is left as a
placeholder using this op. The requisite target-specific parameters for
expanding the size calculation are only available after affinities have been
assigned.
}];
let arguments = (ins
TypeAttr:$encoding,
Stream_ShapeDynamicDims:$encoding_dims,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Size:$storage_size
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$encoding (`{` $encoding_dims^ `}`)?
attr-dict `:` type($storage_size)
}];
let hasVerifier = 1;
}
def Stream_TensorEmptyOp : Stream_PureOp<"tensor.empty", [
Stream_AffinityOp,
DeclareOpInterfaceMethods<Stream_StreamableOp, [
"isMetadata",
"preferCloneToConsumers",
]>,
Stream_TensorPhaseOp,
Util_ShapeAwareOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
]> {
let summary = [{defines an empty tensor value}];
let description = [{
Returns a typed resource initialized with no contents. This still carries
shape metadata and may encode to a non-empty resource such as in cases
where the empty representation still has data (e.g. sparse tensors).
Subsequent writes must populate any ranges of the tensor that are later
read.
}];
let arguments = (ins
TypeAttr:$result_encoding,
Stream_ShapeDynamicDims:$result_encoding_dims,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
`:`
$result_encoding (`` `{` $result_encoding_dims^ `}`)?
`in`
type($result) `` `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return ValueRange{}; }
ValueRange getResultDynamicDims(unsigned idx) { return getResultEncodingDims(); }
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
}
def Stream_TensorConstantOp : Stream_PureOp<"tensor.constant", [
Stream_AffinityOp,
Stream_StreamableOp,
Stream_TensorPhaseOp,
Util_ShapeAwareOp,
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
]> {
let summary = [{defines a constant tensor value}];
let description = [{
Returns a typed resource initialized to the given constant value.
}];
let arguments = (ins
TypedAttrInterface:$value,
TypeAttr:$result_encoding,
Stream_ShapeDynamicDims:$result_encoding_dims,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
`:`
$result_encoding (`` `{` $result_encoding_dims^ `}`)?
`in`
type($result)
`=`
$value
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return ValueRange{}; }
ValueRange getResultDynamicDims(unsigned idx) { return getResultEncodingDims(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_TensorSplatOp : Stream_PureOp<"tensor.splat", [
Stream_AffinityOp,
DeclareOpInterfaceMethods<Stream_StreamableOp, [
"preferCloneToConsumers",
]>,
Stream_TensorPhaseOp,
Util_ShapeAwareOp,
Util_SizeAwareOp,
]> {
let summary = [{splats a value into a shaped tensor}];
let description = [{
Returns a typed resource initialized to the given primitive value.
}];
let arguments = (ins
Stream_PrimitiveType:$value,
TypeAttr:$result_encoding,
Stream_ShapeDynamicDims:$result_encoding_dims,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$value
`:` type($value)
`->`
$result_encoding (`` `{` $result_encoding_dims^ `}`)?
`in`
type($result) `` `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return ValueRange{}; }
ValueRange getResultDynamicDims(unsigned idx) { return getResultEncodingDims(); }
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_TensorCloneOp : Stream_PureOp<"tensor.clone", [
AttrSizedOperandSegments,
Stream_AffinityOp,
Stream_StreamableOp,
Stream_TensorPhaseOp,
Util_ShapeAwareOp,
Util_SizeAwareOp,
]> {
let summary = [{clones the contents of a value}];
let description = [{
Clones the contents of a value at a snapshot in time. Future changes to the
cloned value will not effect the result. Acts as a copy-on-write operation.
}];
let arguments = (ins
Stream_AnyStreamResource:$source,
TypeAttr:$source_encoding,
Stream_ShapeDynamicDims:$source_encoding_dims,
Stream_Size:$source_size,
TypeAttr:$result_encoding,
Stream_ShapeDynamicDims:$result_encoding_dims,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$source `:`
$source_encoding (`` `{` $source_encoding_dims^ `}`)?
`in`
type($source) `` `{` $source_size `}`
`->`
$result_encoding (`` `{` $result_encoding_dims^ `}`)?
`in`
type($result) `` `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return getSourceEncodingDims(); }
ValueRange getResultDynamicDims(unsigned idx) { return getResultEncodingDims(); }
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_TensorSliceOp : Stream_PureOp<"tensor.slice", [
AttrSizedOperandSegments,
Stream_AffinityOp,
Stream_StreamableOp,
Stream_TensorPhaseOp,
Util_ShapeAwareOp,
Util_SizeAwareOp,
]> {
let summary = [{slices out a cloned subview of a value}];
let description = [{
Slices a subrange of a stream resource based on a tensor encoding. Acts as a
copy-on-write operation.
}];
let arguments = (ins
Stream_AnyStreamResource:$source,
TypeAttr:$source_encoding,
Stream_ShapeDynamicDims:$source_encoding_dims,
Stream_Size:$source_size,
Variadic<Stream_Dim>:$start_indices,
Variadic<Stream_Dim>:$lengths,
TypeAttr:$result_encoding,
Stream_ShapeDynamicDims:$result_encoding_dims,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$source `[` $start_indices `for` $lengths `]` `:`
$source_encoding (`` `{` $source_encoding_dims^ `}`)?
`in`
type($source) `` `{` $source_size `}`
`->`
$result_encoding (`` `{` $result_encoding_dims^ `}`)?
`in`
type($result) `` `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return getSourceEncodingDims(); }
ValueRange getResultDynamicDims(unsigned idx) { return getResultEncodingDims(); }
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_TensorFillOp : Stream_Op<"tensor.fill", [
AttrSizedOperandSegments,
AllTypesMatch<["target", "result"]>,
Stream_AffinityOp,
Stream_StreamableOp,
Stream_TensorPhaseOp,
Util_ShapeAwareOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{fills a subview of a stream resource with a value}];
let description = [{
Splats a value into a subview of the given stream resource and returns the
resource with the update applied.
Equivalent to a stream.tensor.splat + stream.tensor.update.
}];
let arguments = (ins
Stream_AnyStreamResource:$target,
TypeAttr:$target_encoding,
Stream_ShapeDynamicDims:$target_encoding_dims,
Stream_Size:$target_size,
Variadic<Stream_Dim>:$start_indices,
Variadic<Stream_Dim>:$lengths,
Stream_PrimitiveType:$value,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$value `,` $target `[` $start_indices `for` $lengths `]` `:`
type($value)
`->`
$target_encoding (`` `{` $target_encoding_dims^ `}`)?
`in`
custom<ShapedTiedResult>(type($target), $target_size)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return getTargetEncodingDims(); }
ValueRange getResultDynamicDims(unsigned idx) { return getTargetEncodingDims(); }
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return getTargetSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_TensorUpdateOp : Stream_Op<"tensor.update", [
AttrSizedOperandSegments,
AllTypesMatch<["target", "result"]>,
Stream_AffinityOp,
Stream_StreamableOp,
Stream_TensorPhaseOp,
Util_ShapeAwareOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{updates a slice of a subview of a resource in-place}];
let description = [{
Copies a value into a resource based on tensor encodings. The returned value
is the entire updated target value.
}];
let arguments = (ins
Stream_AnyStreamResource:$target,
TypeAttr:$target_encoding,
Stream_ShapeDynamicDims:$target_encoding_dims,
Stream_Size:$target_size,
Variadic<Stream_Dim>:$start_indices,
Stream_AnyStreamResource:$update,
TypeAttr:$update_encoding,
Stream_ShapeDynamicDims:$update_encoding_dims,
Stream_Size:$update_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$update `,` $target `[` $start_indices `]` `:`
$update_encoding (`` `{` $update_encoding_dims^ `}`)?
`in`
type($update) `` `{` $update_size `}`
`->`
$target_encoding (`` `{` $target_encoding_dims^ `}`)?
`in`
custom<ShapedTiedResult>(type($target), $target_size)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) {
return idx == 0 ? getTargetEncodingDims() : getUpdateEncodingDims();
}
ValueRange getResultDynamicDims(unsigned idx) { return getTargetEncodingDims(); }
Value getOperandSize(unsigned idx) {
return idx == 0 ? getTargetSize() : getUpdateSize();
}
Value getResultSize(unsigned idx) { return getTargetSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_TensorLoadOp : Stream_PureOp<"tensor.load", [
AttrSizedOperandSegments,
Stream_TensorPhaseOp,
Util_ShapeAwareOp,
Util_SizeAwareOp,
]> {
let summary = [{loads a value from a tensor element}];
let description = [{
Returns the element at the given location from within the tensor.
}];
let arguments = (ins
Stream_StagingResource:$source,
TypeAttr:$source_encoding,
Stream_ShapeDynamicDims:$source_encoding_dims,
Stream_Size:$source_size,
Variadic<Stream_Dim>:$indices
);
let results = (outs
AnyTypeOf<[Stream_PrimitiveType, AnyVector]>:$result
);
let assemblyFormat = [{
$source (`[` $indices^ `]`)? `:`
$source_encoding (`` `{` $source_encoding_dims^ `}`)?
`in`
type($source) `` `{` $source_size `}`
`->`
type($result)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return getSourceEncodingDims(); }
ValueRange getResultDynamicDims(unsigned idx) { return ValueRange{}; }
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_TensorStoreOp : Stream_PureOp<"tensor.store", [
AttrSizedOperandSegments,
AllTypesMatch<["target", "result"]>,
Stream_TensorPhaseOp,
Util_ShapeAwareOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{stores a value into a tensor element}];
let description = [{
Returns a tensor with the element at the given index set to the given value.
}];
let arguments = (ins
Stream_StagingResource:$target,
TypeAttr:$target_encoding,
Stream_ShapeDynamicDims:$target_encoding_dims,
Stream_Size:$target_size,
Variadic<Stream_Dim>:$indices,
AnyTypeOf<[Stream_PrimitiveType, AnyVector]>:$value
);
let results = (outs
Stream_StagingResource:$result
);
let assemblyFormat = [{
$value `,`
$target (`[` $indices^ `]`)? `:`
type($value)
`->`
$target_encoding (`` `{` $target_encoding_dims^ `}`)?
`in`
custom<ShapedTiedResult>(type($target), $target_size)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return getTargetEncodingDims(); }
ValueRange getResultDynamicDims(unsigned idx) { return getTargetEncodingDims(); }
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return getTargetSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_TensorTraceOp : Stream_Op<"tensor.trace", [
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<Util_ShapeAwareOp>,
Util_SizeAwareOp,
]> {
let summary = [{traces one or more tensor values at runtime}];
let description = [{
Traces out to a runtime trace sink (console, log file, etc) the given
tensors. The key is arbitrary and can be used for identifying the set of
values being traced.
}];
let arguments = (ins
StrAttr:$key,
Variadic<Stream_StagingResource>:$resources,
Variadic<Stream_Size>:$resource_sizes,
TypeArrayAttr:$resource_encodings,
Stream_ShapeDynamicDims:$resource_encoding_dims
);
let assemblyFormat = [{
$key `=` `[`
custom<EncodedResourceOperands>(
$resources, type($resources), $resource_sizes,
$resource_encodings, $resource_encoding_dims)
`]` attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getResourceSizes()[idx]; }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
}
} // OpGroupTensorOps
//===----------------------------------------------------------------------===//
// Resource transfer ops
//===----------------------------------------------------------------------===//
def OpGroupResourceTransferOps : OpDocGroup {
let summary = "Resource transfer ops";
let description = "";
}
let opDocGroup = OpGroupResourceTransferOps in {
def Stream_AsyncAllocaOp : Stream_Op<"async.alloca", [
DeclareOpInterfaceMethods<Stream_AffinityOp, [
"getAffinity",
"setAffinity",
]>,
Stream_AsyncPhaseOp,
DeclareOpInterfaceMethods<Stream_StreamableOp, [
"isMetadata",
"preferCloneToConsumers",
]>,
Util_SizeAwareOp,
AlwaysSpeculatable,
MemoryEffects<[MemAlloc]>,
]> {
let summary = [{allocates a transient value with undefined contents}];
let description = [{
Allocates a transient value (one that is short-lived and local to the
current computation) with undefined contents. Consumers of the allocated
result must assume nothing of the contents and use `discard` access.
}];
let arguments = (ins
Stream_Size:$storage_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
attr-dict `:` type($result) `{` $storage_size `}`
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getStorageSize(); }
}];
let hasCanonicalizer = 1;
}
def Stream_AsyncConstantOp : Stream_PureOp<"async.constant", [
Stream_AffinityOp,
Stream_AsyncPhaseOp,
DeclareOpInterfaceMethods<Stream_StreamableOp, [
"isMetadata",
]>,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
]> {
let summary = [{defines a constant resource}];
let description = [{
Returns a new resource with the given constant value.
}];
let arguments = (ins
AnyAttr:$value,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
`:`
type($result) `` `{` $result_size `}`
`=`
$value
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_AsyncSplatOp : Stream_Op<"async.splat", [
Stream_AffinityOp,
Stream_AsyncPhaseOp,
DeclareOpInterfaceMethods<Stream_StreamableOp, [
"preferCloneToConsumers",
]>,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
]> {
let summary = [{splats a value into a resource}];
let description = [{
Returns a new resource with the given primitive value splatted out to fill
the entire contents.
}];
let arguments = (ins
Stream_FillPatternType:$value,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$value `:` type($value) `->` type($result) `` `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return {}; }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_AsyncCloneOp : Stream_Op<"async.clone", [
Stream_AffinityOp,
Stream_AsyncPhaseOp,
DeclareOpInterfaceMethods<Stream_StreamableOp, [
"preferCloneToConsumers",
]>,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
]> {
let summary = [{clones the contents of a value}];
let description = [{
Clones the contents of a value at a snapshot in time. Future changes to the
cloned value will not effect the result. Acts as a copy-on-write operation.
}];
let arguments = (ins
Stream_AnyStreamResource:$source,
Stream_Size:$source_size,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$source `:`
type($source) `` `{` $source_size `}` `->`
type($result) `` `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_AsyncSliceOp : Stream_PureOp<"async.slice", [
AllTypesMatch<["source", "result"]>,
Stream_AffinityOp,
Stream_AsyncPhaseOp,
Stream_StreamableOp,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
]> {
let summary = [{slices out a cloned subview of a value}];
let description = [{
Slices a subrange of a stream resource based on a byte range. Acts as a
copy-on-write operation.
}];
let arguments = (ins
Stream_AnyStreamResource:$source,
Stream_Size:$source_size,
Stream_Offset:$source_offset,
Stream_Offset:$source_end,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$source `[` $source_offset `to` $source_end `]` `:`
type($source) `` `{` $source_size `}` `->`
type($result) `` `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_AsyncFillOp : Stream_Op<"async.fill", [
AllTypesMatch<["target", "result"]>,
Stream_AffinityOp,
Stream_AsyncPhaseOp,
Stream_StreamableOp,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{fills a subview of a stream resource with a value}];
let description = [{
Splats a value into a subview of the given stream resource and returns the
resource with the update applied.
Equivalent to a stream.async.splat + stream.async.update.
}];
let arguments = (ins
Stream_AnyStreamResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Offset:$target_end,
Stream_Size:$target_length,
Stream_FillPatternType:$value,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$value `,`
$target `[` $target_offset `to` $target_end `for` $target_length `]` `:`
type($value) `->`
custom<ShapedTiedResult>(type($target), $target_size)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return getTargetSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_AsyncUpdateOp : Stream_Op<"async.update", [
AllTypesMatch<["target", "result"]>,
Stream_AffinityOp,
Stream_AsyncPhaseOp,
Stream_StreamableOp,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{updates a slice of a subview of a resource in-place}];
let description = [{
Copies a value into a resource based on a byte range. The returned value
is the entire updated target value. Updates can be turned into placement
allocations and avoid copies.
}];
let arguments = (ins
Stream_AnyStreamResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Offset:$target_end,
Stream_AnyStreamResource:$update,
Stream_Size:$update_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$update `,`
$target `[` $target_offset `to` $target_end `]` `:`
type($update) `` `{` $update_size `}` `->`
custom<ShapedTiedResult>(type($target), $target_size)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) {
return idx == 0 ? getTargetSize() : getUpdateSize();
}
Value getResultSize(unsigned idx) { return getTargetSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_AsyncCopyOp : Stream_Op<"async.copy", [
AllTypesMatch<["target", "result"]>,
Stream_AffinityOp,
Stream_AsyncPhaseOp,
Stream_StreamableOp,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{copies a subview of a stream resource to another}];
let description = [{
Copies a subview of a resource into a subview of another.
As with memcpy this does not support overlapping updates into the same
resource. Unlike `stream.async.update` copy sources cannot be allocated
in-place.
Equivalent to a stream.async.slice + stream.async.update.
}];
let arguments = (ins
Stream_AnyStreamResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Offset:$target_end,
Stream_AnyStreamResource:$source,
Stream_Size:$source_size,
Stream_Offset:$source_offset,
Stream_Offset:$source_end,
Stream_Size:$length,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$source `[` $source_offset `to` $source_end `]` `,`
$target `[` $target_offset `to` $target_end `]` `,`
$length `:`
type($source) `` `{` $source_size `}` `->`
custom<ShapedTiedResult>(type($target), $target_size)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) {
return idx == 0 ? getTargetSize() : getSourceSize();
}
Value getResultSize(unsigned idx) { return getTargetSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_AsyncCollectiveOp : Stream_Op<"async.collective", [
AllTypesMatch<["target", "result"]>,
Stream_AffinityOp,
Stream_AsyncPhaseOp,
Stream_StreamableOp,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{performs a collective operation}];
let description = [{
TODO: document different usage. For now this should be considered a
prototype and that modeling of collective operations may change in the
future to better ensure in-place operations (where send/recv is a subset of
recv/send). We may have dedicated operations for the send and recv verbs as
they have sequencing implications - or we could add optional sequencing to
this base op.
}];
let arguments = (ins
Stream_CollectiveAttr:$op,
Stream_AnyStreamResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Offset:$target_end,
Stream_Size:$target_length,
Stream_AnyStreamResource:$source,
Stream_Size:$source_size,
Stream_Offset:$source_offset,
Stream_Offset:$source_end,
Stream_Size:$source_length,
Stream_Size:$element_count,
Stream_Channel:$channel,
Optional<I32>:$param,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_AnyStreamResource:$result
);
let assemblyFormat = [{
`` $op `` `[` $element_count `]`
(`on` `(` $affinity^ `)`)?
`channel` `(` $channel `)`
custom<CollectiveParam>(ref($op), $param) ``
$source `[` $source_offset `to` $source_end `for` $source_length `]` `,`
$target `[` $target_offset `to` $target_end `for` $target_length `]` `:`
type($source) `` `{` $source_size `}` `->`
custom<ShapedTiedResult>(type($target), $target_size)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) {
return idx == 0 ? getTargetSize() : getSourceSize();
}
Value getResultSize(unsigned idx) { return getTargetSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_AsyncTransferOp : Stream_Op<"async.transfer", [
Stream_AffinityOp,
Stream_AsyncPhaseOp,
Stream_StreamableOp,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
]> {
let summary = [{transfers a resource from one location/state to another}];
let description = [{
Transfers a resource between different states (such as a `staging` lifetime
to a `local` lifetime) or different affinities. This is roughly equivalent
to a cast but may have special semantics when later lowered to one or more
devices with discrete memory spaces or pools.
}];
let arguments = (ins
AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>:$source,
Stream_Size:$source_size,
Stream_Size:$result_size,
OptionalAttr<Stream_AffinityAttr>:$source_affinity,
OptionalAttr<Stream_AffinityAttr>:$result_affinity
);
let results = (outs
AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>:$result
);
let assemblyFormat = [{
$source `:` type($source)
`` `{` $source_size `}`
(`from` `(` $source_affinity^ `)`)?
`->`
(`to` `(` $result_affinity^ `)`)?
type($result) `` `{` $result_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return getResultSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_AsyncLoadOp : Stream_PureOp<"async.load", [
Stream_AsyncPhaseOp,
Util_SizeAwareOp,
]> {
let summary = [{loads a value from a resource}];
let description = [{
Returns the element at the given location from within the resource.
}];
let arguments = (ins
Stream_StagingResource:$source,
Stream_Size:$source_size,
Stream_Offset:$source_offset
);
let results = (outs
AnyTypeOf<[Stream_PrimitiveType, AnyVector]>:$result
);
let assemblyFormat = [{
$source `[` $source_offset `]` `:`
type($source) `` `{` $source_size `}`
`->`
type($result)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getSourceSize(); }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_AsyncStoreOp : Stream_PureOp<"async.store", [
AllTypesMatch<["target", "result"]>,
Stream_AsyncPhaseOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{stores a value into a resource}];
let description = [{
Returns a resource with the element at the given offset set to the given
value.
}];
let arguments = (ins
Stream_StagingResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
AnyTypeOf<[Stream_PrimitiveType, AnyVector]>:$value
);
let results = (outs
Stream_StagingResource:$result
);
let assemblyFormat = [{
$value `,`
$target `[` $target_offset `]` `:`
type($value)
`->`
custom<ShapedTiedResult>(type($target), $target_size)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return getTargetSize(); }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_AsyncDispatchOp : Stream_Op<"async.dispatch", [
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
Stream_AffinityOp,
Stream_AsyncPhaseOp,
Stream_StreamableOp,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedOperandsIndexAndLength",
]>,
]> {
let summary = [{dispatches a parallelized grid of work}];
let description = [{
Calls the specified entry point function once for each element in the
specified workgroup count. Each workgroup has access to the same operands
and results and is able to load/store at will.
}];
let arguments = (ins
Variadic<Index>:$workload,
SymbolRefArrayAttr:$entry_points,
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_PrimitiveType,
]>>:$resource_operands,
Variadic<Stream_Size>:$resource_operand_sizes,
Variadic<Stream_Offset>:$resource_operand_offsets,
Variadic<Stream_Offset>:$resource_operand_ends,
Variadic<Stream_Size>:$resource_operand_lengths,
Variadic<Stream_Size>:$result_sizes,
OptionalAttr<Util_TiedOpStorageAttr>:$tied_operands,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Variadic<Stream_AnyStreamResource>:$results
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
custom<DispatchEntryPoints>($entry_points)
(`[` $workload^ `]`)? ``
custom<DispatchOperands>($resource_operands,
$resource_operand_offsets,
$resource_operand_ends,
$resource_operand_lengths) attr-dict `:`
custom<ShapedFunctionType>(ref($resource_operands),
type($resource_operands), $resource_operand_sizes,
type($results), $result_sizes,
$tied_operands)
}];
let extraClassDeclaration = [{
auto getEntryPointRefs() {
return getEntryPoints().getAsRange<SymbolRefAttr>();
}
void forEachEntryPointAttr(std::function<void(SymbolRefAttr)> fn) {
for (auto entryPointAttr : getEntryPointRefs()) fn(entryPointAttr);
}
Value getOperandSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getOperands(), getResourceOperandSizes());
}
Value getResultSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getResults(), getResultSizes());
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
} // OpGroupResourceTransferOps
//===----------------------------------------------------------------------===//
// Async control flow ops
//===----------------------------------------------------------------------===//
def OpGroupAsyncControlFlowOps : OpDocGroup {
let summary = "Async control flow ops";
let description = "";
}
let opDocGroup = OpGroupAsyncControlFlowOps in {
// TODO(benvanik): stream.async.if
// TODO(benvanik): stream.async.select
// TODO(benvanik): stream.async.for
def Stream_AsyncFuncOp : Stream_Op<"async.func", [
CallableOpInterface,
FunctionOpInterface,
IsolatedFromAbove,
Stream_AsyncPhaseOp,
]> {
let summary = [{streamable function declaration}];
let description = [{
Declares a function that can be called as an asynchronous streaming
operation via `stream.async.call`. Today only external functions are
allowed.
}];
let arguments = (ins
SymbolNameAttr:$sym_name,
TypeAttrOf<FunctionType>:$function_type,
OptionalAttr<Util_TiedOpStorageAttr>:$tied_operands,
OptionalAttr<StrAttr>:$sym_visibility,
OptionalAttr<DictArrayAttr>:$arg_attrs,
OptionalAttr<DictArrayAttr>:$res_attrs
);
let regions = (region AnyRegion:$body);
let assemblyFormat = [{
custom<SymbolVisibility>($sym_visibility)
$sym_name
``
custom<ShapedFunctionSignature>($function_type,
$tied_operands,
$arg_attrs,
$res_attrs)
attr-dict-with-keyword
($body^)?
}];
let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins
"StringRef":$name,
"FunctionType":$type,
CArg<"ArrayAttr", "{}">:$tied_operands,
CArg<"ArrayRef<DictionaryAttr>", "{}">:$argAttrs,
CArg<"ArrayRef<DictionaryAttr>", "{}">:$resAttrs
)>,
];
let extraClassDeclaration = [{
static AsyncFuncOp create(Location location, StringRef name,
FunctionType type,
ArrayRef<int64_t> tiedOperands = {},
ArrayRef<DictionaryAttr> argAttrs = {},
ArrayRef<DictionaryAttr> resAttrs = {});
bool isDeclaration() { return true; }
::mlir::Region *getCallableRegion() { return nullptr; }
ArrayRef<Type> getCallableResults() { return getFunctionType().getResults(); }
::mlir::ArrayAttr getCallableArgAttrs() { return getArgAttrs().value_or(nullptr); }
::mlir::ArrayAttr getCallableResAttrs() { return getResAttrs().value_or(nullptr); }
ArrayRef<Type> getArgumentTypes() { return getFunctionType().getInputs(); }
ArrayRef<Type> getResultTypes() { return getFunctionType().getResults(); }
// Returns true if the given result is tied to an argument.
bool isResultTied(int resultIndex);
}];
}
def Stream_AsyncCallOp : Stream_Op<"async.call", [
AttrSizedOperandSegments,
CallOpInterface,
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
Stream_AffinityOp,
Stream_AsyncPhaseOp,
Stream_StreamableOp,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedOperandsIndexAndLength",
]>,
]> {
let summary = [{calls a streamable external host function}];
let description = [{
Calls a function taking/returning resource values with stream semantics.
Asynchronous calls must have no side-effects.
Note that returned resources must have their sizes declared prior to the
call as this is what allows the call to be made on the stream. If external
host logic is required to compute the size (avoid at all costs!) a separate
func.call can be used outside of the stream to do so. If sizes are
unknownable until the operation is performed it should be made as a normal
asynchronous host call with 'coarse-fences' instead.
}];
let arguments = (ins
FlatSymbolRefAttr:$callee,
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_PrimitiveType,
AnyType,
]>>:$resource_operands,
Variadic<Stream_Size>:$resource_operand_sizes,
Variadic<Stream_Offset>:$resource_operand_offsets,
Variadic<Stream_Offset>:$resource_operand_ends,
Variadic<Stream_Size>:$resource_operand_lengths,
Variadic<Stream_Size>:$result_sizes,
OptionalAttr<Util_TiedOpStorageAttr>:$tied_operands,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_PrimitiveType,
]>>:$results
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$callee ``
custom<DispatchOperands>($resource_operands,
$resource_operand_offsets,
$resource_operand_ends,
$resource_operand_lengths) attr-dict `:`
custom<ShapedFunctionType>(ref($resource_operands),
type($resource_operands), $resource_operand_sizes,
type($results), $result_sizes,
$tied_operands)
}];
let extraClassDeclaration = [{
FunctionType getCalleeType();
/// Get the argument operands to the called function.
operand_range getArgOperands() {
return {arg_operand_begin(), arg_operand_end()};
}
operand_iterator arg_operand_begin() { return getResourceOperands().begin(); }
operand_iterator arg_operand_end() { return getResourceOperands().end(); }
/// Return the callee of this operation.
CallInterfaceCallable getCallableForCallee() {
return (*this)->getAttrOfType<SymbolRefAttr>("callee");
}
/// Set the callee for this operation.
void setCalleeFromCallable(CallInterfaceCallable callee) {
(*this)->setAttr("callee", callee.get<SymbolRefAttr>());
}
Value getOperandSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getOperands(), getResourceOperandSizes());
}
Value getResultSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getResults(), getResultSizes());
}
/// Get the argument operands to the called function as a mutable range, this is
/// required by the call interface.
MutableOperandRange getArgOperandsMutable() {
return getResourceOperandsMutable();
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_AsyncExecuteOp : Stream_Op<"async.execute", [
AttrSizedOperandSegments,
RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface, [
"getEntrySuccessorOperands",
]>,
SingleBlockImplicitTerminator<"IREE::Stream::YieldOp">,
Stream_AffinityOp,
Stream_AsyncPhaseOp,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Stream_TimelineOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_ClosureOpInterface>,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResultsIndexAndLength",
]>,
]> {
let summary = [{executes a dependency-aware sequence of streamable ops}];
let description = [{
Evaluates the operations within the region by dependency order while obeying
ties when present. Nested ops execute serially in block order and nested
`stream.async.concurrent` ops can be used to run multiple ops concurrently
within the stream. All resource inputs must be captured explicitly. All
results are only ready once all nested ops complete execution and the
returned timepoint is reached. Zero or more timepoints may be provided to
block execution until they are all reached; zero timepoints indicates that
execution may begin immediately.
}];
let arguments = (ins
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>>:$resource_operands,
Variadic<Stream_Size>:$resource_operand_sizes,
Variadic<Stream_Size>:$result_sizes,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Util_TiedOpStorageAttr>:$tied_operands,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>>:$results,
Stream_Timepoint:$result_timepoint
);
let regions = (region AnyRegion:$body);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`)?
`with` ``
custom<ResourceRegion>($resource_operands,
type($resource_operands), $resource_operand_sizes,
type($results), $result_sizes,
$tied_operands, $body)
`=` `` `>` type($result_timepoint)
attr-dict-with-keyword
}];
let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins
"TypeRange":$resultTypes, "ValueRange":$resultSizes,
"Value":$awaitTimepoint,
"ValueRange":$resourceOperands, "ValueRange":$resourceOperandSizes,
"ArrayRef<int64_t>":$tiedOperands,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>,
];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getOperands(), getResourceOperandSizes());
}
Value getResultSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getResults(), getResultSizes());
}
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_AsyncConcurrentOp : Stream_Op<"async.concurrent", [
ParentOneOf<[
"IREE::Stream::AsyncExecuteOp",
"IREE::Stream::AsyncConcurrentOp",
]>,
AttrSizedOperandSegments,
RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface, [
"getEntrySuccessorOperands",
]>,
SingleBlockImplicitTerminator<"IREE::Stream::YieldOp">,
Stream_AffinityOp,
Stream_AsyncPhaseOp,
Stream_StreamableOp,
DeclareOpInterfaceMethods<Stream_AsyncAccessOp, [
"getAsyncAccessRanges",
]>,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_ClosureOpInterface>,
DeclareOpInterfaceMethods<Util_TiedOpInterface>,
]> {
let summary = [{executes all ops concurrently}];
let description = [{
Represents a wave of work scheduled concurrently (each op executing at the
same time). All resource inputs must be captured explicitly. All results are
only ready once all nested ops complete execution.
Waves can be nested to create a DAG. For example, take the following graph:
```
|
v---------+---------v
+-------|-------+ +-------|-------+
| v--+--v | | v--+--v |
| +----+ +----+ | | +----+ +----+ |
| | %a | | %b | | | | %c | | %d | |
| +----+ +----+ | | +----+ +----+ |
| +--v--+ | | +--v--+ |
+-------|-------+ +-------|-------+
+---------v---------+
|
```
Represented with nested waves:
```mlir
%0 = stream.async.concurrent with(%arg) -> ... {
%1 = stream.async.concurrent with(%arg as %arg0) -> ... {
%a = ...
%b = ...
stream.yield %a, %b
}
%2 = stream.async.concurrent with(%arg as %arg1) -> ... {
%c = ...
%d = ...
stream.yield %c, %d
}
stream.yield %1, %2
}
```
}];
let arguments = (ins
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>>:$resource_operands,
Variadic<Stream_Size>:$resource_operand_sizes,
Variadic<Stream_Size>:$result_sizes,
OptionalAttr<Util_TiedOpStorageAttr>:$tied_operands,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>>:$results
);
let regions = (region AnyRegion:$body);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
`with` ``
custom<ResourceRegion>($resource_operands,
type($resource_operands), $resource_operand_sizes,
type($results), $result_sizes,
$tied_operands, $body)
attr-dict-with-keyword
}];
let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins
"TypeRange":$resultTypes, "ValueRange":$resultSizes,
"ValueRange":$resourceOperands, "ValueRange":$resourceOperandSizes,
"ArrayRef<int64_t>":$tiedOperands,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>,
];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getOperands(), getResourceOperandSizes());
}
Value getResultSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getResults(), getResultSizes());
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
} // OpGroupAsyncControlFlowOps
//===----------------------------------------------------------------------===//
// Explicit command ops
//===----------------------------------------------------------------------===//
def OpGroupExplicitCommandOps : OpDocGroup {
let summary = "Explicit command ops";
let description = "";
}
let opDocGroup = OpGroupExplicitCommandOps in {
def Stream_CmdFlushOp : Stream_Op<"cmd.flush", [
Stream_CmdPhaseOp,
Stream_StreamableOp,
Stream_SubviewEffectOp,
Util_SizeAwareOp,
]> {
let summary = [{flushes a subview of a resource}];
let description = [{
Transfers a resource to an external target. The resource memory is made
available to the target and can be made visible there using
`stream.cmd.invalidate`.
}];
let arguments = (ins
Stream_AnyResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Size:$target_length,
OptionalAttr<Stream_AffinityAttr>:$source_affinity
);
let results = (outs);
let assemblyFormat = [{
(`to` `(` $source_affinity^ `)`)?
$target `[` $target_offset `for` $target_length `]` `:`
type($target) `` `{` $target_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_CmdInvalidateOp : Stream_Op<"cmd.invalidate", [
Stream_CmdPhaseOp,
Stream_StreamableOp,
Stream_SubviewEffectOp,
Util_SizeAwareOp,
]> {
let summary = [{invalidates a subview of a resource}];
let description = [{
Transfers a resource from an external source into the current target. The
resource memory is assumed to have been made available at the source via
`stream.cmd.flush`.
}];
let arguments = (ins
Stream_AnyResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Size:$target_length,
OptionalAttr<Stream_AffinityAttr>:$source_affinity
);
let results = (outs);
let assemblyFormat = [{
(`from` `(` $source_affinity^ `)`)?
$target `[` $target_offset `for` $target_length `]` `:`
type($target) `` `{` $target_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_CmdDiscardOp : Stream_Op<"cmd.discard", [
Stream_CmdPhaseOp,
Stream_StreamableOp,
Stream_SubviewEffectOp,
Util_SizeAwareOp,
]> {
let summary = [{discards a subview of a resource}];
let description = [{
Discards a subview of a resource, indicating that after this command the
specified contents are no longer needed. This can be used to trim memory
or invalidate caches.
}];
let arguments = (ins
Stream_AnyResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Size:$target_length
);
let results = (outs);
let assemblyFormat = [{
$target `[` $target_offset `for` $target_length `]` `:`
type($target) `` `{` $target_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_CmdFillOp : Stream_Op<"cmd.fill", [
Stream_CmdPhaseOp,
Stream_StreamableOp,
Stream_SubviewEffectOp,
Util_SizeAwareOp,
]> {
let summary = [{fills a subview of a stream resource with a value}];
let description = [{
Splats a value into a subview of the given stream resource and returns the
resource with the update applied.
}];
let arguments = (ins
Stream_AnyStreamResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Size:$target_length,
Stream_NativeFillPatternType:$value
);
let results = (outs);
let assemblyFormat = [{
$value `,`
$target `[` $target_offset `for` $target_length `]` `:`
type($value) `->`
type($target) `` `{` $target_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getTargetSize(); }
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_CmdCopyOp : Stream_Op<"cmd.copy", [
Stream_CmdPhaseOp,
Stream_StreamableOp,
Stream_SubviewEffectOp,
Util_SizeAwareOp,
]> {
let summary = [{copies a subview of a stream resource to another}];
let description = [{
Copies a subview of a resource into a subview of another.
As with memcpy this does not support overlapping updates into the same
resource.
}];
let arguments = (ins
Stream_AnyResource:$source,
Stream_Size:$source_size,
Stream_Offset:$source_offset,
Stream_AnyResource:$target,
Stream_Size:$target_size,
Stream_Offset:$target_offset,
Stream_Size:$length
);
let results = (outs);
let assemblyFormat = [{
$source `[` $source_offset `]` `,`
$target `[` $target_offset `]` `,`
$length `:`
type($source) `` `{` $source_size `}` `->`
type($target) `` `{` $target_size `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) {
return idx == 0 ? getSourceSize() : getTargetSize();
}
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_CmdCollectiveOp : Stream_Op<"cmd.collective", [
AttrSizedOperandSegments,
Stream_CmdPhaseOp,
Stream_StreamableOp,
Stream_SubviewEffectOp,
Util_SizeAwareOp,
]> {
let summary = [{dispatches a collective operation}];
let description = [{
Dispatches a collective operation specified against the device. If grouped
with other collectives in a `stream.cmd.concurrent` region the collective
operations may fuse and execute more efficiently.
}];
let arguments = (ins
Stream_CollectiveAttr:$op,
Stream_Channel:$channel,
Stream_Size:$element_count,
Optional<I32>:$param,
Variadic<Stream_AnyStreamResource>:$resources,
Variadic<Stream_Size>:$resource_sizes,
Variadic<Stream_Offset>:$resource_offsets,
Variadic<Stream_Size>:$resource_lengths,
Stream_ResourceAccessArrayAttr:$resource_accesses
);
let results = (outs);
let assemblyFormat = [{
`` $op `` `[` $element_count `]`
`channel` `(` $channel `)`
(`param` `(` $param^ `:` type($param) `)`)? `{`
custom<DispatchResources>($resources, type($resources), $resource_sizes,
$resource_offsets, $resource_lengths,
$resource_accesses)
`\n` `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) {
return IREE::Util::findValueSizeInList(
idx - getODSOperandIndexAndLength(4).first,
getResources(), getResourceSizes());
}
Value getResultSize(unsigned idx) { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_CmdDispatchOp : Stream_Op<"cmd.dispatch", [
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
Stream_CmdPhaseOp,
Stream_StreamableOp,
Stream_SubviewEffectOp,
Util_SizeAwareOp,
]> {
let summary = [{dispatches a parallelized grid of work}];
let description = [{
Calls the specified entry point function once for each element in the
specified workgroup count. Each workgroup has access to the same operands
and results and is able to load/store at will.
}];
let arguments = (ins
Variadic<Index>:$workload,
SymbolRefArrayAttr:$entry_points,
Variadic<Stream_PrimitiveType>:$uniform_operands,
Variadic<Stream_AnyStreamResource>:$resources,
Variadic<Stream_Size>:$resource_sizes,
Variadic<Stream_Offset>:$resource_offsets,
Variadic<Stream_Size>:$resource_lengths,
Stream_ResourceAccessArrayAttr:$resource_accesses
);
let results = (outs);
let assemblyFormat = [{
custom<DispatchEntryPoints>($entry_points)
(`[` $workload^ `]`)? ``
(`(` $uniform_operands^ `:` type($uniform_operands) `)`)? `{`
custom<DispatchResources>($resources, type($resources), $resource_sizes,
$resource_offsets, $resource_lengths,
$resource_accesses)
`\n` `}`
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
auto getEntryPointRefs() {
return getEntryPoints().getAsRange<SymbolRefAttr>();
}
void forEachEntryPointAttr(std::function<void(SymbolRefAttr)> fn) {
for (auto entryPointAttr : getEntryPointRefs()) fn(entryPointAttr);
}
Value getOperandSize(unsigned idx) {
return IREE::Util::findValueSizeInList(
idx - getODSOperandIndexAndLength(2).first,
getResources(), getResourceSizes());
}
Value getResultSize(unsigned idx) { return {}; }
// Builds a map of operand index to argument index.
static SmallVector<unsigned> makeOperandToArgMap(mlir::FunctionOpInterface funcOp);
// Builds a map of resource to argument index of the corresponding binding.
static SmallVector<unsigned> makeResourceToArgMap(mlir::FunctionOpInterface funcOp);
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_CmdFuncOp : Stream_Op<"cmd.func", [
CallableOpInterface,
FunctionOpInterface,
IsolatedFromAbove,
Stream_CmdPhaseOp,
]> {
let summary = [{streamable function declaration}];
let description = [{
Declares a function that can be called as an asynchronous streaming
operation via `stream.cmd.call`. Today only external functions are
allowed.
}];
let arguments = (ins
SymbolNameAttr:$sym_name,
TypeAttrOf<FunctionType>:$function_type,
OptionalAttr<StrAttr>:$sym_visibility,
OptionalAttr<DictArrayAttr>:$arg_attrs,
OptionalAttr<DictArrayAttr>:$res_attrs
);
let regions = (region AnyRegion:$body);
let assemblyFormat = [{
custom<SymbolVisibility>($sym_visibility)
$sym_name ``
custom<DispatchFunctionSignature>($function_type,
$arg_attrs,
$res_attrs)
attr-dict-with-keyword
($body^)?
}];
let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins
"StringRef":$name,
"FunctionType":$type,
CArg<"ArrayRef<DictionaryAttr>", "{}">:$argAttrs,
CArg<"ArrayRef<DictionaryAttr>", "{}">:$resAttrs
)>,
];
let extraClassDeclaration = [{
static CmdFuncOp create(Location location, StringRef name,
FunctionType type,
ArrayRef<DictionaryAttr> argAttrs = {},
ArrayRef<DictionaryAttr> resAttrs = {});
bool isDeclaration() { return true; }
::mlir::Region *getCallableRegion() { return nullptr; }
ArrayRef<Type> getCallableResults() { return getFunctionType().getResults(); }
::mlir::ArrayAttr getCallableArgAttrs() { return getArgAttrs().value_or(nullptr); }
::mlir::ArrayAttr getCallableResAttrs() { return getResAttrs().value_or(nullptr); }
ArrayRef<Type> getArgumentTypes() { return getFunctionType().getInputs(); }
ArrayRef<Type> getResultTypes() { return getFunctionType().getResults(); }
}];
}
def Stream_CmdCallOp : Stream_Op<"cmd.call", [
AttrSizedOperandSegments,
CallOpInterface,
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
Stream_CmdPhaseOp,
Stream_StreamableOp,
Stream_SubviewEffectOp,
Util_SizeAwareOp,
]> {
let summary = [{calls a streamable external host function}];
let description = [{
Calls a function operating on resource values with stream semantics.
Asynchronous calls must have no side-effects.
}];
let arguments = (ins
FlatSymbolRefAttr:$callee,
// TODO(benvanik): rework this to make more sense - it's mostly a copy of
// stream.cmd.dispatch mashed together with stream.async.call and it's kind
// of confusing.
Variadic<AnyTypeOf<[
Stream_PrimitiveType,
Stream_AnyStreamResource,
AnyType, // for custom types
]>>:$resource_operands,
Variadic<Stream_Size>:$resource_operand_sizes,
Variadic<Stream_Offset>:$resource_operand_offsets,
Variadic<Stream_Size>:$resource_operand_lengths,
Variadic<Stream_Size>:$result_sizes,
OptionalAttr<Util_TiedOpStorageAttr>:$tied_operands,
Stream_ResourceAccessArrayAttr:$resource_operand_accesses
);
let results = (outs
Variadic<Stream_PrimitiveType>:$results
);
let assemblyFormat = [{
$callee ``
custom<CmdCallOperands>($resource_operands,
$resource_operand_offsets,
$resource_operand_lengths,
$resource_operand_accesses) attr-dict `:`
custom<ShapedFunctionType>(ref($resource_operands),
type($resource_operands),
$resource_operand_sizes,
type($results),
$result_sizes,
$tied_operands)
}];
let extraClassDeclaration = [{
/// Get the argument operands to the called function.
operand_range getArgOperands() {
return {arg_operand_begin(), arg_operand_end()};
}
operand_iterator arg_operand_begin() { return getOperands().begin(); }
operand_iterator arg_operand_end() { return getOperands().end(); }
/// Return the callee of this operation.
CallInterfaceCallable getCallableForCallee() {
return (*this)->getAttrOfType<SymbolRefAttr>("callee");
}
/// Set the callee for this operation.
void setCalleeFromCallable(CallInterfaceCallable callee) {
(*this)->setAttr("callee", callee.get<SymbolRefAttr>());
}
Value getOperandSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getOperands(), getResourceOperandSizes());
}
Value getResultSize(unsigned idx) { return {}; }
/// Get the argument operands to the called function as a mutable range, this is
/// required by the call interface.
MutableOperandRange getArgOperandsMutable() {
return getResourceOperandsMutable();
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_CmdExecuteOp : Stream_Op<"cmd.execute", [
AttrSizedOperandSegments,
RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface, [
"getEntrySuccessorOperands",
]>,
SingleBlockImplicitTerminator<"IREE::Stream::YieldOp">,
Stream_AffinityOp,
Stream_CmdPhaseOp,
Stream_TimelineOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_ClosureOpInterface>,
]> {
let summary = [{executes a dependency-aware sequence of streamable ops}];
let description = [{
Evaluates the operations within the region by dependency order while obeying
ties when present. Nested ops execute serially in block order and nested
`stream.cmd.concurrent` ops can be used to run multiple ops concurrently
within the stream. All resource inputs must be captured explicitly. All
results are only ready once all nested ops complete execution and the
returned timepoint is reached. Zero or more timepoints may be provided to
block execution until they are all reached; zero timepoints indicates that
execution may begin immediately.
}];
let arguments = (ins
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>>:$resource_operands,
Variadic<Stream_Size>:$resource_operand_sizes,
Optional<Stream_Timepoint>:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let regions = (region AnyRegion:$body);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`await` `(` $await_timepoint^ `)` `=` `` `>`)?
`with` ``
custom<ExplicitResourceRegion>($resource_operands,
type($resource_operands), $resource_operand_sizes,
$body)
`=` `` `>` type($result_timepoint)
attr-dict-with-keyword
}];
let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins
"Value":$awaitTimepoint,
"ValueRange":$resourceOperands, "ValueRange":$resourceOperandSizes,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>,
];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getOperands(), getResourceOperandSizes());
}
Value getResultSize(unsigned idx) {
return {};
}
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_CmdSerialOp : Stream_Op<"cmd.serial", [
ParentOneOf<[
"IREE::Stream::CmdExecuteOp",
"IREE::Stream::CmdSerialOp",
"IREE::Stream::CmdConcurrentOp",
]>,
RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
SingleBlockImplicitTerminator<"IREE::Stream::YieldOp">,
Stream_CmdPhaseOp,
Stream_StreamableOp,
]> {
let summary = [{executes all ops serially (in-order)}];
let description = [{
Represents a sequence of work scheduled serially (each op executing one
after the other).
Regions can be nested to create a DAG. For example, take the following graph:
```
|
v---------+-----v
+-------|-------+ +---|----+
| v--+--v | | v |
| +----+ +----+ | | +----+ |
| | @a | | @b | | | | @c | |
| +----+ +----+ | | +----+ |
| | | | | | |
| | | | | +-v--+ |
| | | | | | @d | |
| | | | | +----+ |
| +--v--+ | | | |
+-------|-------+ +---|----+
+---------v-----+
|
```
Represented with nested regions:
```mlir
stream.cmd.concurrent {
stream.cmd.concurrent {
stream.cmd.dispatch @a
stream.cmd.dispatch @b
}
stream.cmd.serial {
stream.cmd.dispatch @c
stream.cmd.dispatch @d
}
}
```
}];
let arguments = (ins);
let results = (outs);
let regions = (region AnyRegion:$body);
let assemblyFormat = [{
$body
attr-dict-with-keyword
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_CmdConcurrentOp : Stream_Op<"cmd.concurrent", [
ParentOneOf<[
"IREE::Stream::CmdExecuteOp",
"IREE::Stream::CmdSerialOp",
"IREE::Stream::CmdConcurrentOp",
]>,
RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
SingleBlockImplicitTerminator<"IREE::Stream::YieldOp">,
Stream_CmdPhaseOp,
Stream_StreamableOp,
]> {
let summary = [{executes all ops concurrently}];
let description = [{
Represents a wave of work scheduled concurrently (each op executing at the
same time).
Waves can be nested to create a DAG. For example, take the following graph:
```
|
v---------+---------v
+-------|-------+ +-------|-------+
| v--+--v | | v--+--v |
| +----+ +----+ | | +----+ +----+ |
| | @a | | @b | | | | @c | | @d | |
| +----+ +----+ | | +----+ +----+ |
| +--v--+ | | +--v--+ |
+-------|-------+ +-------|-------+
+---------v---------+
|
```
Represented with nested waves:
```mlir
stream.cmd.concurrent {
stream.cmd.concurrent {
stream.cmd.dispatch @a
stream.cmd.dispatch @b
}
stream.cmd.concurrent {
stream.cmd.dispatch @c
stream.cmd.dispatch @d
}
}
```
}];
let arguments = (ins);
let results = (outs);
let regions = (region AnyRegion:$body);
let assemblyFormat = [{
$body
attr-dict-with-keyword
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
} // OpGroupExplicitCommandOps
//===----------------------------------------------------------------------===//
// Synchronization ops
//===----------------------------------------------------------------------===//
def OpGroupSynchronizationOps : OpDocGroup {
let summary = "Synchronization ops";
let description = "";
}
let opDocGroup = OpGroupSynchronizationOps in {
def Stream_TimepointImmediateOp : Stream_PureOp<"timepoint.immediate", [
ConstantLike,
Stream_TimelineOp,
]> {
let summary = [{results an immediately-available timepoint}];
let description = [{
Timepoints indicate a point in the execution timeline and this op can be
used to get a placeholder representing the start of the timeline. Any waits
on the returned timepoint will resolve immediately. This generally folds
away but can be useful if needing to initialize globals or branch args.
}];
let arguments = (ins);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
attr-dict
`=` `` `>` type($result_timepoint)
}];
let extraClassDeclaration = [{
SmallVector<Value> getAwaitTimepoints() { return {}; }
}];
let hasFolder = 1;
}
def Stream_TimepointImportOp : Stream_PureOp<"timepoint.import", [
Stream_AffinityOp,
]> {
let summary = [{imports a timepoint from an external dialect type}];
let description = [{
Defines a conversion from an external dialect type such as `hal.semaphore`
that is resolved during lowering into the stream dialect. This can be used
to interoperate between levels of the stack that require specifying stream
types and those that prior to lowering do not handle them.
}];
let arguments = (ins
Variadic<AnyType>:$operands,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$operands `:` `(` type($operands) `)`
`=` `` `>`
type($result_timepoint)
attr-dict-with-keyword
}];
}
def Stream_TimepointExportOp : Stream_PureOp<"timepoint.export", [
Stream_AffinityOp,
]> {
let summary = [{exports a timepoint to an external dialect type}];
let description = [{
Defines a conversion to an external dialect type such as `hal.fence`
that is resolved during lowering into the stream dialect. This can be used
to interoperate between levels of the stack that require specifying stream
types and those that prior to lowering do not handle them.
}];
let arguments = (ins
Stream_Timepoint:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Variadic<AnyType>:$results
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$await_timepoint
`=` `` `>`
`(` type($results) `)`
attr-dict-with-keyword
}];
let hasFolder = 1;
}
def Stream_TimepointChainExternalOp :
Stream_Op<"timepoint.chain_external", [
Stream_AffinityOp,
]> {
let summary = [{exports a timepoint to an external dialect type}];
let description = [{
Defines a conversion to an external dialect type such as `hal.fence`
that is resolved during lowering into the stream dialect. This can be used
to interoperate between levels of the stack that require specifying stream
types and those that prior to lowering do not handle them.
}];
let arguments = (ins
Stream_Timepoint:$await_timepoint,
Variadic<AnyType>:$external_values,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$await_timepoint
`=` `` `>`
`(` $external_values `:` type($external_values) `)`
attr-dict-with-keyword
}];
let hasCanonicalizer = 1;
}
def Stream_TimepointJoinOp : Stream_PureOp<"timepoint.join", [
Stream_TimelineOp,
]> {
let summary = [{joins one or more timepoints into the max of all of them}];
let description = [{
Returns a timepoint that indicates that all of the input timepoints have
been reached.
}];
let arguments = (ins
Variadic<Stream_Timepoint>:$await_timepoints
);
let results = (outs
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
`max` `(` $await_timepoints `)` `=` `` `>` type($result_timepoint)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
// Joins one or more timepoints and returns a new timepoint representing a
// point on the timeline where all timepoints have been resolved.
static Value join(Location loc, ValueRange timepoints, OpBuilder &builder);
static Value join(ValueRange timepoints, OpBuilder &builder);
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
def Stream_TimepointBarrierOp : Stream_PureOp<"timepoint.barrier", [
AllTypesMatch<["resource", "result"]>,
Stream_AffinityOp,
Stream_TimelineOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResult",
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
"getTiedResultsIndexAndLength",
]>,
]> {
let summary = [{returns a timepoint indicating when a resource is available}];
let description = [{
After asynchronous execution scheduling resources may exist in different
states at different points in the execution timeline. This op enables
identifying when the version of a resource after a particular point in the
timeline is available. As timepoints transitively chain the timepoint must
only cover the resource availability but not be limited to its original
production timepoint.
}];
let arguments = (ins
AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>:$resource,
Stream_Size:$resource_size,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>:$result,
Stream_Timepoint:$result_timepoint
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$resource `:` type($resource) `` `{` $resource_size `}`
`=` `` `>`
type($result_timepoint)
attr-dict-with-keyword
}];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) { return getResourceSize(); }
Value getResultSize(unsigned idx) { return getResourceSize(); }
SmallVector<Value> getAwaitTimepoints() { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
}
def Stream_TimepointAwaitOp : Stream_PureOp<"timepoint.await", [
AttrSizedOperandSegments,
Stream_AffinityOp,
Stream_TimelineOp,
Util_SizeAwareOp,
DeclareOpInterfaceMethods<Util_TiedOpInterface, [
"getTiedResultOperandIndex",
"getTiedResultOperandIndices",
]>,
]> {
let summary = [{awaits a timepoint before returning a set of resources}];
let description = [{
After asynchronous execution scheduling resources may exist in different
states at different points in the execution timeline. This op enables
resolving the version of a resource after a particular point in the
timeline. As timepoints transitively chain the timepoint must only cover the
resource availability but not be limited to its original production
timepoint.
}];
let arguments = (ins
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>>:$resource_operands,
Variadic<Stream_Size>:$resource_operand_sizes,
Stream_Timepoint:$await_timepoint,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>>:$results
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
$await_timepoint `=` `` `>`
$resource_operands `:`
custom<ShapedTypeList>(type($resource_operands),
type($results), $resource_operand_sizes)
attr-dict-with-keyword
}];
let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins
"ValueRange":$resourceOperands, "ValueRange":$resourceOperandSizes,
"Value":$await_timepoint,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>,
];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) {
return getResourceOperandSizes()[idx];
}
Value getResultSize(unsigned idx) {
return getResourceOperandSizes()[idx];
}
SmallVector<Value> getAwaitTimepoints() {
if (getAwaitTimepoint()) return {getAwaitTimepoint()}; else return {};
}
Value getResultTimepoint() { return {}; }
}];
let hasVerifier = 1;
let hasCanonicalizer = 1;
let hasFolder = 1;
}
} // OpGroupSynchronizationOps
//===----------------------------------------------------------------------===//
// Channels
//===----------------------------------------------------------------------===//
def OpGroupChannelOps : OpDocGroup {
let summary = "Channel ops";
let description = "";
}
let opDocGroup = OpGroupChannelOps in {
def Stream_ChannelCreateOp : Stream_PureOp<"channel.create", [
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
Stream_AffinityOp,
]> {
let summary = [{creates a new channel for collective communication}];
let description = [{
Returns a new channel with the given rank associated with the specified
affinity. Collective operations using this channel must only be submitted on
compatible affinities.
The group and ID are optional and may be null. The rank and count can be
omitted to indicate a default inherited from the environment or device
configuration at runtime.
}];
let arguments = (ins
Optional<Util_BufferType>:$id,
OptionalAttr<StrAttr>:$group,
Optional<Index>:$rank,
Optional<Index>:$count,
OptionalAttr<Stream_AffinityAttr>:$affinity
);
let results = (outs
Stream_Channel:$result
);
let assemblyFormat = [{
(`on` `(` $affinity^ `)`)?
(`id` `(` $id^ `)`)?
(`group` `(` $group^ `)`)?
(`rank` `(` $rank^ `)`)?
(`count` `(` $count^ `)`)?
`:` type($result)
attr-dict-with-keyword
}];
let hasCanonicalizer = 1;
}
def Stream_ChannelSplitOp : Stream_PureOp<"channel.split", [
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>
]> {
let summary = [{splits a collective communication channel}];
let description = [{
Partitions the group associated with the given channel into disjoint
subgroups for each unique value of color. Each new subgroup contains all
participants of the same color and within each subgroup the key argument
is used to define the rank order. When multiple participants in a group
use the same key the tie will be broken using their rank in the parent
group. A color of -1 indicates that the rank does not participate in any
subgroup and will return a null channel.
}];
let arguments = (ins
Stream_Channel:$channel,
Index:$color,
Index:$key
);
let results = (outs
Stream_Channel:$result
);
let assemblyFormat = [{
$channel `,` $color `,` $key
`:` type($channel) `->` type($result)
attr-dict-with-keyword
}];
let hasCanonicalizer = 1;
}
def Stream_ChannelRankOp : Stream_PureOp<"channel.rank", [
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
]> {
let summary = [{returns the rank of the local participant in the group}];
let description = [{
Returns the rank the channel represents as a participant in a collective
group in `[0, count)`.
}];
let arguments = (ins
Stream_Channel:$channel
);
let results = (outs
Index:$result
);
let assemblyFormat = [{
$channel `:` type($result)
attr-dict-with-keyword
}];
let hasFolder = 1;
}
def Stream_ChannelCountOp : Stream_PureOp<"channel.count", [
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
]> {
let summary = [{returns the total number of participants in the group}];
let description = [{
Returns the total participant count in the collective communicator group.
}];
let arguments = (ins
Stream_Channel:$channel
);
let results = (outs
Index:$result
);
let assemblyFormat = [{
$channel `:` type($result)
attr-dict-with-keyword
}];
let hasFolder = 1;
}
} // OpGroupChannelOps
//===----------------------------------------------------------------------===//
// Executables
//===----------------------------------------------------------------------===//
def OpGroupExecutableOps : OpDocGroup {
let summary = "Executable ops";
let description = "";
}
let opDocGroup = OpGroupExecutableOps in {
def Stream_ExecutableOp : Stream_Op<"executable", [
IsolatedFromAbove,
SingleBlockImplicitTerminator<"IREE::Stream::ExecutableEndOp">,
Symbol,
SymbolTable,
Util_ObjectLike,
]> {
let summary = [{generic executable module}];
let description = [{
An executable module containing one or more public functions. The contents
of the functions are safe to dispatch and can be lowered further to
target-specific backend IR representations.
}];
let arguments = (ins
OptionalAttr<StrAttr>:$sym_visibility,
SymbolNameAttr:$sym_name
);
let regions = (region AnyRegion:$body);
let assemblyFormat = [{
custom<SymbolVisibility>($sym_visibility)
$sym_name
attr-dict-with-keyword
regions
}];
let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins "StringRef":$sym_name)>,
];
let extraClassDeclaration = [{
Block& getBlock() { return getBody().front(); }
::mlir::ModuleOp getInnerModule() {
auto it = getBlock().getOps<::mlir::ModuleOp>();
if (it.empty()) return {};
return *it.begin();
}
}];
let hasVerifier = 1;
}
def Stream_ExecutableEndOp : Stream_Op<"executable.end", [
HasParent<"IREE::Stream::ExecutableOp">,
Terminator,
]> {
let summary = [{terminator pseudo-op for the executable op}];
let assemblyFormat = "attr-dict";
}
def Stream_ExecutableExportOp : Stream_Op<"executable.export", [
HasParent<"IREE::Stream::ExecutableOp">,
Symbol,
IsolatedFromAbove,
]> {
let summary = [{defines an executable entry point for dispatch operations}];
let description = [{
Specifies an exported function with an externally-visible alias. Multiple
exports can reference the same internal function.
Each entry point can have a unique workgroup count calculation region.
This region takes the workload parameters passed to each flow.dispatch and
produces an XYZ workgroup count for the 3D grid dispatch.
}];
let arguments = (ins
OptionalAttr<StrAttr>:$sym_visibility,
SymbolNameAttr:$sym_name,
FlatSymbolRefAttr:$function_ref
);
let regions = (region AnyRegion:$workgroup_count);
let assemblyFormat = [{
custom<SymbolVisibility>($sym_visibility)
custom<SymbolAlias>($sym_name, $function_ref)
custom<WorkgroupCountRegion>($workgroup_count)
attr-dict-with-keyword
}];
let builders = [
OpBuilder<(ins
"StringRef":$sym_name,
"FlatSymbolRefAttr":$function_ref
)>,
];
let extraClassDeclaration = [{
::mlir::FunctionOpInterface lookupFunctionRef();
}];
let hasVerifier = 1;
}
def Stream_BindingSubspanOp : Stream_PureOp<"binding.subspan", [
Util_ShapeAwareOp,
]> {
let summary = [{returns an alias to a subspan of interface binding data}];
let description = [{
Returns a subview to a tensor or memref-like type from a binding. The same
binding may have multiple subviews at different byte offsets.
}];
let arguments = (ins
Stream_AnyBinding:$binding,
Stream_Offset:$byte_offset,
Stream_ShapeDynamicDims:$dynamic_dims
);
let results = (outs
AnyType:$result
);
let assemblyFormat = [{
$binding `` `[` $byte_offset `]`
attr-dict `:` type($binding) `->` type($result) (`{` $dynamic_dims^ `}`)?
}];
let hasVerifier = 1;
let extraClassDeclaration = [{
ValueRange getOperandDynamicDims(unsigned idx) { return ValueRange{}; }
ValueRange getResultDynamicDims(unsigned idx) { return getDynamicDims(); }
}];
}
def Stream_DispatchWorkgroupIDOp : Stream_PureOp<"dispatch.workgroup.id", [
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
]> {
let summary = [{returns the index of the current workgroup in the grid}];
let description = [{
The global workgroup ID of the current workgroup in the range of
`[0, stream.dispatch.workgroup.count)` along each dimension.
Represented as a 3D grid classically written as XYZ.
Corresponds to the `WorkgroupId` SPIR-V built-in and the `blockIdx` CUDA
built-in variable.
```mlir
%x = stream.dispatch.workgroup.id[0] : index
%y = stream.dispatch.workgroup.id[1] : index
%z = stream.dispatch.workgroup.id[2] : index
```
}];
let arguments = (ins IndexAttr:$dimension);
let results = (outs Stream_Dim:$result);
let assemblyFormat = "`[` $dimension `]` attr-dict `:` type($result)";
let builders = [
OpBuilder<(ins "unsigned":$dim),
[{
build($_builder, $_state, $_builder.getIndexType(), $_builder.getIndexAttr(dim));
}]>,
];
let hasVerifier = 1;
}
def Stream_DispatchWorkgroupCountOp : Stream_PureOp<"dispatch.workgroup.count", [
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
]> {
let summary = [{returns the total workgroup count of the grid}];
let description = [{
The total number of workgroups along each dimension in the dispatch grid.
Represented as a 3D grid classically written as XYZ.
Corresponds to the `NumWorkgroups` SPIR-V built-in and the `gridDim` CUDA
built-in variable.
```mlir
%x = stream.dispatch.workgroup.count[0] : index
%y = stream.dispatch.workgroup.count[1] : index
%z = stream.dispatch.workgroup.count[2] : index
```
}];
let arguments = (ins IndexAttr:$dimension);
let results = (outs Stream_Dim:$result);
let assemblyFormat = "`[` $dimension `]` attr-dict `:` type($result)";
let builders = [
OpBuilder<(ins "unsigned":$dim),
[{
build($_builder, $_state, $_builder.getIndexType(), $_builder.getIndexAttr(dim));
}]>,
];
let hasVerifier = 1;
}
def Stream_DispatchWorkgroupSizeOp : Stream_PureOp<"dispatch.workgroup.size", [
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
]> {
let summary = [{returns the size of each workgroup in invocations}];
let description = [{
The number of local invocations within the current workgroup along each
dimension. Depending on backend this may map to the SIMT thread count or
inner loop nest parameters.
Workgroup sizes are not determined at the stream dialect level as they are
dependent on the target backend determined when lowering into the HAL. It's
still possible to use the symbolic workgroup size inside of dispatch
executables as a placeholder for the resolved value once in the HAL.
Represented as a 3D grid classically written as XYZ.
Corresponds to the `WorkgroupSize` SPIR-V built-in and the `blockDim` CUDA
built-in variable.
```mlir
%x = stream.dispatch.workgroup.size[0] : index
%y = stream.dispatch.workgroup.size[1] : index
%z = stream.dispatch.workgroup.size[2] : index
```
}];
let arguments = (ins IndexAttr:$dimension);
let results = (outs Stream_Dim:$result);
let assemblyFormat = "`[` $dimension `]` attr-dict `:` type($result)";
let builders = [
OpBuilder<(ins "unsigned":$dim),
[{
build($_builder, $_state, $_builder.getIndexType(), $_builder.getIndexAttr(dim));
}]>,
];
let hasVerifier = 1;
}
} // OpGroupExecutableOps
//===----------------------------------------------------------------------===//
// Misc
//===----------------------------------------------------------------------===//
def OpGroupMiscellaneousOps : OpDocGroup {
let summary = "Miscellaneous ops";
let description = "";
}
let opDocGroup = OpGroupMiscellaneousOps in {
def Stream_ReturnOp : Stream_Op<"return", [
ParentOneOf<[
"IREE::Stream::ExecutableExportOp",
]>,
Pure,
ReturnLike,
Terminator,
]> {
let summary = [{returns results from a region}];
let description = [{
The values returned are copied by-value.
}];
let arguments = (ins
Variadic<AnyType>:$operands
);
let assemblyFormat = [{
attr-dict
$operands `:` type($operands)
}];
}
def Stream_YieldOp : Stream_Op<"yield", [
ParentOneOf<[
"IREE::Stream::AsyncExecuteOp",
"IREE::Stream::AsyncConcurrentOp",
"IREE::Stream::CmdExecuteOp",
"IREE::Stream::CmdSerialOp",
"IREE::Stream::CmdConcurrentOp",
]>,
Pure,
DeclareOpInterfaceMethods<RegionBranchTerminatorOpInterface>,
Terminator,
SameVariadicOperandSize,
Util_SizeAwareOp,
]> {
let summary = [{yields stream values from an execution region}];
let description = [{
The values returned represent the asynchronous value at the point in time
the SSA value is defined (or tied).
}];
let arguments = (ins
Variadic<AnyTypeOf<[
Stream_AnyStreamResource,
Stream_StagingResource,
]>>:$resource_operands,
Variadic<Index>:$resource_operand_sizes
);
let assemblyFormat = [{
attr-dict
($resource_operands^ `:`
custom<ShapedTypeList>(type($resource_operands),
$resource_operand_sizes))?
}];
let builders = [
OpBuilder<(ins),
[{
build($_builder, $_state, ValueRange{}, ValueRange{});
}]>,
];
let extraClassDeclaration = [{
Value getOperandSize(unsigned idx) {
return IREE::Util::findValueSizeInList(idx, getOperands(), getResourceOperandSizes());
}
Value getResultSize(unsigned idx) { return {}; }
}];
}
} // OpGroupMiscellaneousOps
#endif // IREE_DIALECT_STREAM_OPS