blob: bbbbfad6eda91833b693455428b3e2de19381090 [file] [log] [blame]
//===- LinalgOps.td - Linalg dialect ops -------------------*- tablegen -*-===//
//
// Copyright 2019 The MLIR Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
//
// This is the operation definition file for linear algebra operations.
//
//===----------------------------------------------------------------------===//
include "mlir/Linalg/IR/LinalgBase.td"
#ifdef LINALG_OPS
#else
#define LINALG_OPS
// Base class for Linalg dialect ops that do not correspond to library calls.
class Linalg_Op<string mnemonic, list<OpTrait> traits = []> :
Op<Linalg_Dialect, mnemonic, traits> {
// For every linalg op, there needs to be a:
// * void print(OpAsmPrinter *p, ${C++ class of Op} op)
// * LogicalResult verify(${C++ class of Op} op)
// * ParseResult parse${C++ class of Op}(OpAsmParser *parser,
// OperationState *result)
// functions.
let printer = [{ return ::print(p, *this); }];
let verifier = [{ return ::verify(*this); }];
let parser = [{ return ::parse$cppClass(parser, result); }];
}
def BufferAllocOp :
Linalg_Op<"buffer_alloc">,
Arguments<(ins Variadic<Index>:$size)>,
Results<(outs Buffer)> {
let summary = "buffer allocation operation";
let description = [{
The "buffer_alloc" op creates a 1-D linalg.buffer of the specified type,
upon which a base view can be laid out to give it indexing semantics.
"buffer_alloc" takes a single argument, the size of the buffer to allocate
(in number of elements).
```{.mlir}
%0 = linalg.buffer_alloc(%arg0) : !linalg.buffer<?xf32>
```
The size argument may be omitted if it is statically known, in which case it
must be reflected in the type.
```{.mlir}
%0 = linalg.buffer_alloc() : !linalg.buffer<4xf32>
```
}];
let builders = [OpBuilder<
"Builder *builder, OperationState *result, BufferType bufferType", [{
result->types.push_back(bufferType);
}]
>];
let extraClassDeclaration = [{
BufferType getBufferType() { return getType().cast<BufferType>(); }
Type getElementType() { return getBufferType().getElementType(); }
}];
}
def BufferDeallocOp :
Linalg_Op<"buffer_dealloc">,
Arguments<(ins Buffer:$buffer)>,
Results<(outs)> {
let summary = "buffer allocation operation";
let description = [{
The "buffer_dealloc" op frees a 1-D linalg.buffer of the specified type.
```{.mlir}
linalg.buffer_dealloc %0 : !linalg.buffer<f32>
```
}];
let builders = [OpBuilder<
"Builder *builder, OperationState *result, BufferType bufferType", [{
result->types.push_back(bufferType);
}]
>];
let extraClassDeclaration = [{
BufferType getBufferType() {
return getOperand()->getType().cast<BufferType>();
}
}];
// Fully specified by traits.
let verifier = ?;
}
def BufferSizeOp :
Linalg_Op<"buffer_size", [NoSideEffect]>,
Arguments<(ins Buffer)>,
Results<(outs Index)> {
let summary = "buffer size operation";
let description = [{
The "linalg.buffer_size" operation takes a linalg.buffer and returns an
"index". For example:
%0 = linalg.buffer_size %arg0 : !linalg.buffer<f32>
}];
// Fully specified by traits.
let verifier = ?;
}
def DimOp : Linalg_Op<"dim", [NoSideEffect]>,
Arguments<(ins View:$view, APIntAttr:$index)>,
Results<(outs Index)> {
let summary = "dimension index operation";
let description = [{
The "linalg.dim" operation takes a linalg.view and returns an
"index". It requires a single integer attribute named "index". It
returns the size of the specified dimension. For example:
%1 = linalg.dim %0, 2 : view<?x?x?xf32>
}];
let verifier = [{
if (getIndex() >= getViewType().getRank())
return emitOpError("index is out of range");
return success();
}];
let builders = [OpBuilder<
"Builder *builder, OperationState *result, Value *view, unsigned index",
[{
result->addOperands(view);
result->addAttribute(
"index", builder->getIntegerAttr(builder->getIndexType(), index));
result->types.push_back(builder->getIndexType());
}]>];
let extraClassDeclaration = [{
unsigned getIndex() {
return getAttrOfType<IntegerAttr>("index").getValue().getZExtValue();
}
ViewType getViewType() { return getOperand()->getType().cast<ViewType>(); }
}];
let hasCanonicalizer = 1;
}
def SubViewOp : Linalg_Op<"subview", [NoSideEffect]>,
Arguments<(ins View:$view, Variadic<Index>:$ranges)>,
Results<(outs View)> {
let summary = "subview operation";
let description = [{
The "linalg.subview" operation takes a linalg.view, a list of indices and
returns a new linalg.view of the same type that is contained within the
operand view.
This operation is equivalent to a non-rank-reducing slice operation. The
main difference is the operands are all of type `index` and no intermediate
linalg.range operations are required. A "linalg.subview" is thus a
specialized linalg.slice with a higher level of abstraction.
%1 = linalg.subview %0[%1, %2, %3, %4, %5, %6] : view<?x?xf32>
}];
// TODO(ntv) evolve syntax towards:
// linalg.subview %0[%1:%2:%3][%4:%5:%6] : view<?x?xf32>
let builders = [OpBuilder<
"Builder *builder, OperationState *result, Value *view, "
"ArrayRef<Value *> ranges",
[{
result->addOperands(view);
result->addOperands(ranges);
result->types.push_back(view->getType());
}]>];
let verifier = [{
auto numRanges = (getNumOperands() - 1) / 3;
if (getNumOperands() != 3 * numRanges + 1 ||
numRanges != getViewType().getRank())
return emitOpError("expected a view followed by 3 indices specifying ") <<
"a range for each dimension";
return success();
}];
let extraClassDeclaration = [{
Value *getView() { return getOperand(0); }
ViewType getViewType() { return getView()->getType().cast<ViewType>(); }
struct Range { Value *min; Value *max; Value *step; };
Range getRange(unsigned i) {
return Range{
getOperand(1 + 3*i), getOperand(1 + 3*i + 1), getOperand(1 + 3*i + 2)};
}
SmallVector<Range, 8> getRanges() {
SmallVector<Range, 8> res;
unsigned rank = getViewType().getRank();
res.reserve(rank);
for (unsigned i = 0; i < rank; ++i)
res.push_back(getRange(i));
return res;
}
// This requires `SubViewOp` to be declared, in the future it should be
// folded into the builders.
static void build(Builder *builder, OperationState *result, Value *view,
ArrayRef<SubViewOp::Range> ranges) {
result->addOperands(view);
for (auto r : ranges)
result->addOperands({r.min, r.max, r.step});
result->types.push_back(view->getType());
}
}];
}
def YieldOp : Linalg_Op<"yield", [NativeOpTrait<"IsTerminator">]>,
Arguments<(ins Variadic<AnyType>:$values)> {
let summary = "Linalg yield operation";
let description = [{
"linalg.yield" is a special terminator operation for blocks inside regions
in linalg ops. It returns values to the immediately enclosing linalg op.
linalg.yield %f0, %f1 : f32, f32
}];
}
#endif // LINALG_OPS