1//===- Ops.td - Toy dialect operation definitions ----------*- tablegen -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Defines the operations of the Toy dialect.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef TOY_OPS
14#define TOY_OPS
15
16include "mlir/Interfaces/SideEffectInterfaces.td"
17
18// Provide a definition of the 'toy' dialect in the ODS framework so that we
19// can define our operations.
20def Toy_Dialect : Dialect {
21  let name = "toy";
22  let cppNamespace = "::mlir::toy";
23}
24
25// Base class for toy dialect operations. This operation inherits from the base
26// `Op` class in OpBase.td, and provides:
27//   * The parent dialect of the operation.
28//   * The mnemonic for the operation, or the name without the dialect prefix.
29//   * A list of traits for the operation.
30class Toy_Op<string mnemonic, list<OpTrait> traits = []> :
31    Op<Toy_Dialect, mnemonic, traits>;
32
33//===----------------------------------------------------------------------===//
34// Toy Operations
35//===----------------------------------------------------------------------===//
36
37// We define a toy operation by inheriting from our base 'Toy_Op' class above.
38// Here we provide the mnemonic and a list of traits for the operation. The
39// constant operation is marked as 'NoSideEffect' as it is a pure operation
40// and may be removed if dead.
41def ConstantOp : Toy_Op<"constant", [NoSideEffect]> {
42  // Provide a summary and description for this operation. This can be used to
43  // auto-generate documentation of the operations within our dialect.
44  let summary = "constant";
45  let description = [{
46    Constant operation turns a literal into an SSA value. The data is attached
47    to the operation as an attribute. For example:
48
49    ```mlir
50      %0 = toy.constant dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]>
51                        : tensor<2x3xf64>
52    ```
53  }];
54
55  // The constant operation takes an attribute as the only input.
56  let arguments = (ins F64ElementsAttr:$value);
57
58  // The constant operation returns a single value of TensorType.
59  let results = (outs F64Tensor);
60
61  // Specify a parser and printer method.
62  let parser = [{ return ::parseConstantOp(parser, result); }];
63  let printer = [{ return ::print(p, *this); }];
64
65  // Add custom build methods for the constant operation. These method populates
66  // the `state` that MLIR uses to create operations, i.e. these are used when
67  // using `builder.create<ConstantOp>(...)`.
68  let builders = [
69    // Build a constant with a given constant tensor value.
70    OpBuilder<(ins "DenseElementsAttr":$value), [{
71      build($_builder, $_state, value.getType(), value);
72    }]>,
73
74    // Build a constant with a given constant floating-point value.
75    OpBuilder<(ins "double":$value)>
76  ];
77
78  // Invoke a static verify method to verify this constant operation.
79  let verifier = [{ return ::verify(*this); }];
80}
81
82def AddOp : Toy_Op<"add", [NoSideEffect]> {
83  let summary = "element-wise addition operation";
84  let description = [{
85    The "add" operation performs element-wise addition between two tensors.
86    The shapes of the tensor operands are expected to match.
87  }];
88
89  let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
90  let results = (outs F64Tensor);
91
92  // Specify a parser and printer method.
93  let parser = [{ return ::parseBinaryOp(parser, result); }];
94  let printer = [{ return ::printBinaryOp(p, *this); }];
95
96  // Allow building an AddOp with from the two input operands.
97  let builders = [
98    OpBuilder<(ins "Value":$lhs, "Value":$rhs)>
99  ];
100}
101
102def GenericCallOp : Toy_Op<"generic_call"> {
103  let summary = "generic call operation";
104  let description = [{
105    Generic calls represent calls to a user defined function that needs to
106    be specialized for the shape of its arguments. The callee name is attached
107    as a symbol reference via an attribute. The arguments list must match the
108    arguments expected by the callee. For example:
109
110    ```mlir
111     %4 = toy.generic_call @my_func(%1, %3)
112           : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
113    ```
114
115    This is only valid if a function named "my_func" exists and takes two
116    arguments.
117  }];
118
119  // The generic call operation takes a symbol reference attribute as the
120  // callee, and inputs for the call.
121  let arguments = (ins FlatSymbolRefAttr:$callee, Variadic<F64Tensor>:$inputs);
122
123  // The generic call operation returns a single value of TensorType.
124  let results = (outs F64Tensor);
125
126  // Specialize assembly printing and parsing using a declarative format.
127  let assemblyFormat = [{
128    $callee `(` $inputs `)` attr-dict `:` functional-type($inputs, results)
129  }];
130
131  // Add custom build methods for the generic call operation.
132  let builders = [
133    OpBuilder<(ins "StringRef":$callee, "ArrayRef<Value>":$arguments)>
134  ];
135}
136
137def MulOp : Toy_Op<"mul", [NoSideEffect]> {
138  let summary = "element-wise multiplication operation";
139  let description = [{
140    The "mul" operation performs element-wise multiplication between two
141    tensors. The shapes of the tensor operands are expected to match.
142  }];
143
144  let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
145  let results = (outs F64Tensor);
146
147  // Specify a parser and printer method.
148  let parser = [{ return ::parseBinaryOp(parser, result); }];
149  let printer = [{ return ::printBinaryOp(p, *this); }];
150
151  // Allow building a MulOp with from the two input operands.
152  let builders = [
153    OpBuilder<(ins "Value":$lhs, "Value":$rhs)>
154  ];
155}
156
157def PrintOp : Toy_Op<"print"> {
158  let summary = "print operation";
159  let description = [{
160    The "print" builtin operation prints a given input tensor, and produces
161    no results.
162  }];
163
164  // The print operation takes an input tensor to print.
165  let arguments = (ins F64Tensor:$input);
166
167  let assemblyFormat = "$input attr-dict `:` type($input)";
168}
169
170def ReshapeOp : Toy_Op<"reshape", [NoSideEffect]> {
171  let summary = "tensor reshape operation";
172  let description = [{
173    Reshape operation is transforming its input tensor into a new tensor with
174    the same number of elements but different shapes. For example:
175
176    ```mlir
177       %0 = toy.reshape (%arg1 : tensor<10xf64>) to tensor<5x2xf64>
178    ```
179  }];
180
181  let arguments = (ins F64Tensor:$input);
182
183  // We expect that the reshape operation returns a statically shaped tensor.
184  let results = (outs StaticShapeTensorOf<[F64]>);
185
186  let assemblyFormat = [{
187    `(` $input `:` type($input) `)` attr-dict `to` type(results)
188  }];
189
190  // Enable registering canonicalization patterns with this operation.
191  let hasCanonicalizer = 1;
192}
193
194def ReturnOp : Toy_Op<"return", [NoSideEffect, HasParent<"FuncOp">,
195                                 Terminator]> {
196  let summary = "return operation";
197  let description = [{
198    The "return" operation represents a return operation within a function.
199    The operation takes an optional tensor operand and produces no results.
200    The operand type must match the signature of the function that contains
201    the operation. For example:
202
203    ```mlir
204      func @foo() -> tensor<2xf64> {
205        ...
206        toy.return %0 : tensor<2xf64>
207      }
208    ```
209  }];
210
211  // The return operation takes an optional input operand to return. This
212  // value must match the return type of the enclosing function.
213  let arguments = (ins Variadic<F64Tensor>:$input);
214
215  // The return operation only emits the input in the format if it is present.
216  let assemblyFormat = "($input^ `:` type($input))? attr-dict ";
217
218  // Allow building a ReturnOp with no return operand.
219  let builders = [
220    OpBuilder<(ins), [{ build($_builder, $_state, llvm::None); }]>
221  ];
222
223  // Provide extra utility definitions on the c++ operation class definition.
224  let extraClassDeclaration = [{
225    bool hasOperand() { return getNumOperands() != 0; }
226  }];
227
228  // Invoke a static verify method to verify this return operation.
229  let verifier = [{ return ::verify(*this); }];
230}
231
232def TransposeOp : Toy_Op<"transpose", [NoSideEffect]> {
233  let summary = "transpose operation";
234
235  let arguments = (ins F64Tensor:$input);
236  let results = (outs F64Tensor);
237
238  let assemblyFormat = [{
239    `(` $input `:` type($input) `)` attr-dict `to` type(results)
240  }];
241
242  // Enable registering canonicalization patterns with this operation.
243  let hasCanonicalizer = 1;
244
245  // Allow building a TransposeOp with from the input operand.
246  let builders = [
247    OpBuilder<(ins "Value":$input)>
248  ];
249
250  // Invoke a static verify method to verify this transpose operation.
251  let verifier = [{ return ::verify(*this); }];
252}
253
254#endif // TOY_OPS
255