blob: a8912e4137b868cc4e57878de67f417336921311 [file]
// Copyright 2019 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_UTIL_IR_UTIL_OPS
#define IREE_DIALECT_UTIL_IR_UTIL_OPS
include "iree/compiler/Dialect/Util/IR/UtilBase.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
//===----------------------------------------------------------------------===//
// Op types
//===----------------------------------------------------------------------===//
class Util_Op<string mnemonic, list<OpTrait> traits = []> :
Op<Util_Dialect, mnemonic, traits> {
let parser = [{ return parse$cppClass(parser, result); }];
let printer = [{ print$cppClass(p, *this); }];
}
class Util_PureOp<string mnemonic, list<OpTrait> traits = []> :
Util_Op<mnemonic, !listconcat(traits, [NoSideEffect])>;
//===----------------------------------------------------------------------===//
// Byte buffers and host data
//===----------------------------------------------------------------------===//
def Util_NullOp : Util_PureOp<"null"> {
let summary = "a null type value";
let description = [{
Defines an SSA value that is lowered into dialects supporting
null/undefined/optional/etc values.
}];
let results = (outs
AnyType:$result
);
let assemblyFormat = [{
attr-dict `:` type($result)
}];
}
def Util_ByteBufferConstantOp : Util_PureOp<"byte_buffer.constant"> {
let summary = "constant host-side byte buffer";
let description = [{
Defines a compile-time byte buffer based on the given attribute value.
The attribute will be serialized into the canonical IREE format for the
chosen host target.
}];
let arguments = (ins
ElementsAttr:$value
);
let results = (outs
ByteBufferType:$result
);
let assemblyFormat = [{
attr-dict `:` type($result) `=` $value
}];
}
//===----------------------------------------------------------------------===//
// Compiler hints
//===----------------------------------------------------------------------===//
def Util_DoNotOptimizeOp : Util_Op<"do_not_optimize"> {
let summary = "Prevents compiler optimizations of a value.";
let description = [{
Wraps any operands in an unoptimizable identity. This operation is declared
as having side effects, so no compiler optimizations will be able to reason
about it. This prevents its results from being folded. It will be dropped as
the final step in compilation.
}];
let arguments = (ins Variadic<AnyType>:$arguments);
let results = (outs Variadic<AnyType>:$results);
let verifier = [{ return verify$cppClass(*this); }];
let builders = [
OpBuilder<(ins "ValueRange":$operands,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>
];
}
// TODO(gcmn): It shouldn't be necessary to have both of these ops. Unify the
// approach here.
def Util_DynamicShapeConstantOp : Util_Op<"dynamic_shape_constant"> {
let summary = "A tensor constant that can have dynamic dimensions";
let description = [{
Allows specifying a constant where the return value can erase shape
information. This operation is declared as having side effects and has no
folder, so will not be optimized away by the compiler. The underlying shape
information should be hidden from the compiler and resolved at runtime.
```mlir
%c = util.dynamic_shape_constant tensor<2x2xf32> -> tensor<?x?xf32>
%res = "mhlo.abs"(%c) : (tensor<?x?xf32>) -> tensor<?x?xf32>
```
}];
let arguments = (ins ElementsAttr:$value);
let results = (outs AnyTensor:$result);
let assemblyFormat = "$value attr-dict `->` type($result)";
}
def Util_UnfoldableConstantOp : Util_Op<"unfoldable_constant"> {
let summary = "A constant that cannot be folded by the compiler.";
let description = [{
Similar to a std.constant, but is declared as having a side effect and has
no folder. This is really just syntactic sugar as it is canonicalized to a
std.constant wrapped in an util.do_not_optimize.
}];
let arguments = (ins AnyAttr:$value);
let results = (outs AnyType);
let builders = [
OpBuilder<(ins "Attribute":$value),
[{ build($_builder, $_state, value.getType(), value); }]>];
let hasCanonicalizer = 1;
}
def Util_UnreachableOp : Util_Op<"unreachable", [NoSideEffect, Terminator]> {
let summary = [{unreachable assertion op}];
let description = [{
Signals to the compiler that the parent block should not be reachable.
This may be converted into a runtime assertion, though ideally they are
stripped during translation.
```mlir
^bb0:
%true = constant true
cond_br %true, ^bb2, ^bb1
^bb1:
// Indicates that this branch should never be taken.
util.unreachable "shouldn't be here"
^bb2:
...
```
}];
let arguments = (ins StrAttr:$message);
let assemblyFormat = "$message attr-dict";
}
//===----------------------------------------------------------------------===//
// Lists
//===----------------------------------------------------------------------===//
// NOTE: this type is mostly just a placeholder. Ideally we'd make this
// immutable and have operations like util.list.append/concat/etc the returned
// new SSA values. This would make optimizing the list usage much easier and
// enable hoisting/CSE of list access/mutation.
def Util_ListCreateOp : Util_PureOp<
"list.create", [MemoryEffects<[MemAlloc]>]> {
let summary = [{creates a new empty list}];
let description = [{
Creates a new empty list with an optional initial capacity.
}];
let arguments = (ins
Optional<Index>:$initial_capacity
);
let results = (outs
AnyList:$result
);
let assemblyFormat = "($initial_capacity^)? attr-dict `:` type($result)";
}
def Util_ListSizeOp : Util_Op<"list.size", [MemoryEffects<[MemRead]>]> {
let summary = [{the size of the list in elements}];
let description = [{
Returns the current size of the list in elements.
}];
let arguments = (ins
AnyList:$list
);
let results = (outs
Index:$result
);
let assemblyFormat = "operands attr-dict `:` type($list)";
}
def Util_ListResizeOp : Util_Op<"list.resize", [MemoryEffects<[MemWrite]>]> {
let summary = [{resizes the list to a new count in elements}];
let description = [{
Resizes the list to contain `new_size` elements. This will either truncate
the list if the existing size is greater than `new_size` or extend the list
with the default list value of the element type.
}];
let arguments = (ins
AnyList:$list,
Index:$new_size
);
let assemblyFormat = "operands attr-dict `:` type($list)";
}
def Util_ListGetOp : Util_Op<"list.get", [MemoryEffects<[MemRead]>]> {
let summary = [{element accessor}];
let description = [{
Returns the value of the element at the given index. Note that the value
may be null if the element is null or the type does not match.
}];
let arguments = (ins
AnyList:$list,
Index:$index
);
let results = (outs
AnyType:$result
);
let assemblyFormat = "$list `[` $index `]` attr-dict `:` custom<ListTypeGet>(type($list), type($result))";
let verifier = [{ return verify$cppClass(*this); }];
}
def Util_ListSetOp : Util_Op<"list.set", [MemoryEffects<[MemWrite]>]> {
let summary = [{element mutator}];
let description = [{
Sets the element at the given index to the new value.
}];
let arguments = (ins
AnyList:$list,
Index:$index,
AnyType:$value
);
let assemblyFormat = "$list `[` $index `]` `,` $value attr-dict `:` custom<ListTypeSet>(type($list), type($value))";
let verifier = [{ return verify$cppClass(*this); }];
}
#endif // IREE_DIALECT_UTIL_IR_UTIL_OPS