}
```
-Above we introduce several of the concepts for defining operations in the ODS
-framework, but there are many more that we haven't had a chance to: regions,
-variadic operands, etc. Check out the
-[full specification](../../OpDefinitions.md) for more details.
-
-## Complete Toy Example
+#### Specifying a Custom Assembly Format
At this point we can generate our "Toy IR". A simplified version of the previous
example:
} loc("test/codegen.toy":0:0)
```
+One thing to notice here is that all of our Toy operations are printed using the
+generic assembly format. This format is the one shown when breaking down
+`toy.transpose` at the beginning of this chapter. MLIR allows for operations to
+define their own custom assembly format, either
+[declaratively](../../OpDefinitions.md#declarative-assembly-format) or
+imperatively via C++. Defining a custom assembly format allows for tailoring the
+generated IR into something a bit more readable by removing a lot of the fluff
+that is required by the generic format. Let's walk through an example of an
+operation format that we would like to simplify.
+
+##### `toy.print`
+
+The current form of `toy.print` is a little verbose. There are a lot of
+additional characters that we would like to strip away. Let's begin by thinking
+of what a good format of `toy.print` would be, and see how we can implement it.
+Looking at the basics of `toy.print` we get:
+
+```mlir
+toy.print %5 : tensor<*xf64> loc(...)
+```
+
+Here we have stripped much of the format down to the bare essentials, and it has
+become much more readable. To provide a custom assembly format, an operation can
+either override the `parser` and `printer` fields for a C++ format, or the
+`assemblyFormat` field for the declarative format. Let's look at the C++ variant
+first, as this is what the declarative format maps to internally.
+
+```tablegen
+/// Consider a stripped definition of `toy.print` here.
+def PrintOp : Toy_Op<"print"> {
+ let arguments = (ins F64Tensor:$input);
+
+ // Divert the printer and parser to static functions in our .cpp
+ // file that correspond to 'print' and 'printPrintOp'. 'printer' and 'parser'
+ // here correspond to an instance of a 'OpAsmParser' and 'OpAsmPrinter'. More
+ // details on these classes is shown below.
+ let printer = [{ return ::print(printer, *this); }];
+ let parser = [{ return ::parse$cppClass(parser, result); }];
+}
+```
+
+A C++ implementation for the printer and parser is shown below:
+
+```c++
+/// The 'OpAsmPrinter' class is a stream that will allows for formatting
+/// strings, attributes, operands, types, etc.
+static void print(mlir::OpAsmPrinter &printer, PrintOp op) {
+ printer << "toy.print " << op.input();
+ printer.printOptionalAttrDict(op.getAttrs());
+ printer << " : " << op.input().getType();
+}
+
+/// The 'OpAsmPrinter' class provides a collection of methods for parsing
+/// various punctuation, as well as attributes, operands, types, etc. Each of
+/// these methods returns a `ParseResult`. This class is a wrapper around
+/// `LogicalResult` that can be converted to a boolean `true` value on failure,
+/// or `false` on success. This allows for easily chaining together a set of
+/// parser rules. These rules are used to populate an `mlir::OperationState`
+/// similarly to the `build` methods described above.
+static mlir::ParseResult parsePrintOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ // Parse the input operand, the attribute dictionary, and the type of the
+ // input.
+ mlir::OpAsmParser::OperandType inputOperand;
+ mlir::Type inputType;
+ if (parser.parseOperand(inputOperand) ||
+ parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
+ parser.parseType(inputType))
+ return mlir::failure();
+
+ // Resolve the input operand to the type we parsed in.
+ if (parser.resolveOperand(inputOperand, inputType, result.operands))
+ return mlir::failure();
+
+ return mlir::success();
+}
+```
+
+With the C++ implementation defined, let's see how this can be mapped to the
+[declarative format](../../OpDefinitions.md#declarative-assembly-format). The
+declarative format is largely composed of three different components:
+
+* Directives
+ - A type of builtin function, with an optional set of arguments.
+* Literals
+ - A keyword or punctuation surrounded by \`\`.
+* Variables
+ - An entity that has been registered on the operation itself, i.e. an
+ argument(attribute or operand), result, successor, etc. In the `PrintOp`
+ example above, a variable would be `$input`.
+
+A direct mapping of our C++ format looks something like:
+
+```tablegen
+/// Consider a stripped definition of `toy.print` here.
+def PrintOp : Toy_Op<"print"> {
+ let arguments = (ins F64Tensor:$input);
+
+ // In the following format we have two directives, `attr-dict` and `type`.
+ // These correspond to the attribute dictionary and the type of a given
+ // variable represectively.
+ let assemblyFormat = "$input attr-dict `:` type($input)";
+}
+```
+
+The [declarative format](../../OpDefinitions.md#declarative-assembly-format) has
+many more interesting features, so be sure to check it out before implementing a
+custom format in C++. After beautifying the format of a few of our operations we
+now get a much more readable:
+
+```mlir
+module {
+ func @multiply_transpose(%arg0: tensor<*xf64>, %arg1: tensor<*xf64>) -> tensor<*xf64> {
+ %0 = toy.transpose(%arg0 : tensor<*xf64>) to tensor<*xf64> loc("test/codegen.toy":5:10)
+ %1 = toy.transpose(%arg1 : tensor<*xf64>) to tensor<*xf64> loc("test/codegen.toy":5:25)
+ %2 = toy.mul %0, %1 : tensor<*xf64> loc("test/codegen.toy":5:25)
+ toy.return %2 : tensor<*xf64> loc("test/codegen.toy":5:3)
+ } loc("test/codegen.toy":4:1)
+ func @main() {
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64> loc("test/codegen.toy":9:17)
+ %1 = toy.reshape(%0 : tensor<2x3xf64>) to tensor<2x3xf64> loc("test/codegen.toy":9:3)
+ %2 = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64> loc("test/codegen.toy":10:17)
+ %3 = toy.reshape(%2 : tensor<6xf64>) to tensor<2x3xf64> loc("test/codegen.toy":10:3)
+ %4 = toy.generic_call @multiply_transpose(%1, %3) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64> loc("test/codegen.toy":11:11)
+ %5 = toy.generic_call @multiply_transpose(%3, %1) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64> loc("test/codegen.toy":12:11)
+ toy.print %5 : tensor<*xf64> loc("test/codegen.toy":13:3)
+ toy.return loc("test/codegen.toy":8:1)
+ } loc("test/codegen.toy":8:1)
+} loc("test/codegen.toy":0:0)
+```
+
+Above we introduce several of the concepts for defining operations in the ODS
+framework, but there are many more that we haven't had a chance to: regions,
+variadic operands, etc. Check out the
+[full specification](../../OpDefinitions.md) for more details.
+
+## Complete Toy Example
+
+At this point we can generate our "Toy IR". A simplified version of the previous
+example:
+
+```toy
+# User defined generic function that operates on unknown shaped arguments.
+def multiply_transpose(a, b) {
+ return transpose(a) * transpose(b);
+}
+
+def main() {
+ var a<2, 3> = [[1, 2, 3], [4, 5, 6]];
+ var b<2, 3> = [1, 2, 3, 4, 5, 6];
+ var c = multiply_transpose(a, b);
+ var d = multiply_transpose(b, a);
+ print(d);
+}
+```
+
+Results in the following IR:
+
+```mlir
+module {
+ func @multiply_transpose(%arg0: tensor<*xf64>, %arg1: tensor<*xf64>) -> tensor<*xf64> {
+ %0 = toy.transpose(%arg0 : tensor<*xf64>) to tensor<*xf64> loc("test/codegen.toy":5:10)
+ %1 = toy.transpose(%arg1 : tensor<*xf64>) to tensor<*xf64> loc("test/codegen.toy":5:25)
+ %2 = toy.mul %0, %1 : tensor<*xf64> loc("test/codegen.toy":5:25)
+ toy.return %2 : tensor<*xf64> loc("test/codegen.toy":5:3)
+ } loc("test/codegen.toy":4:1)
+ func @main() {
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64> loc("test/codegen.toy":9:17)
+ %1 = toy.reshape(%0 : tensor<2x3xf64>) to tensor<2x3xf64> loc("test/codegen.toy":9:3)
+ %2 = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64> loc("test/codegen.toy":10:17)
+ %3 = toy.reshape(%2 : tensor<6xf64>) to tensor<2x3xf64> loc("test/codegen.toy":10:3)
+ %4 = toy.generic_call @multiply_transpose(%1, %3) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64> loc("test/codegen.toy":11:11)
+ %5 = toy.generic_call @multiply_transpose(%3, %1) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64> loc("test/codegen.toy":12:11)
+ toy.print %5 : tensor<*xf64> loc("test/codegen.toy":13:3)
+ toy.return loc("test/codegen.toy":8:1)
+ } loc("test/codegen.toy":8:1)
+} loc("test/codegen.toy":0:0)
+```
+
You can build `toyc-ch2` and try yourself: `toyc-ch2
test/Examples/Toy/Ch2/codegen.toy -emit=mlir -mlir-print-debuginfo`. We can also
check our RoundTrip: `toyc-ch2 test/Examples/Toy/Ch2/codegen.toy -emit=mlir
```mlir
func @transpose_transpose(%arg0: tensor<*xf64>) -> tensor<*xf64> {
- %0 = "toy.transpose"(%arg0) : (tensor<*xf64>) -> tensor<*xf64>
- %1 = "toy.transpose"(%0) : (tensor<*xf64>) -> tensor<*xf64>
- "toy.return"(%1) : (tensor<*xf64>) -> ()
+ %0 = toy.transpose(%arg0 : tensor<*xf64>) to tensor<*xf64>
+ %1 = toy.transpose(%0 : tensor<*xf64>) to tensor<*xf64>
+ toy.return %1 : tensor<*xf64>
}
```
```mlir
func @transpose_transpose(%arg0: tensor<*xf64>) -> tensor<*xf64> {
- %0 = "toy.transpose"(%arg0) : (tensor<*xf64>) -> tensor<*xf64>
- "toy.return"(%arg0) : (tensor<*xf64>) -> ()
+ %0 = toy.transpose(%arg0 : tensor<*xf64>) to tensor<*xf64>
+ toy.return %arg0 : tensor<*xf64>
}
```
```mlir
func @transpose_transpose(%arg0: tensor<*xf64>) -> tensor<*xf64> {
- "toy.return"(%arg0) : (tensor<*xf64>) -> ()
+ toy.return %arg0 : tensor<*xf64>
}
```
```mlir
module {
func @main() {
- %0 = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00]> : tensor<2xf64>}
- : () -> tensor<2xf64>
- %1 = "toy.reshape"(%0) : (tensor<2xf64>) -> tensor<2x1xf64>
- %2 = "toy.reshape"(%1) : (tensor<2x1xf64>) -> tensor<2x1xf64>
- %3 = "toy.reshape"(%2) : (tensor<2x1xf64>) -> tensor<2x1xf64>
- "toy.print"(%3) : (tensor<2x1xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[1.000000e+00, 2.000000e+00]> : tensor<2xf64>
+ %1 = toy.reshape(%0 : tensor<2xf64>) to tensor<2x1xf64>
+ %2 = toy.reshape(%1 : tensor<2x1xf64>) to tensor<2x1xf64>
+ %3 = toy.reshape(%2 : tensor<2x1xf64>) to tensor<2x1xf64>
+ toy.print %3 : tensor<2x1xf64>
+ toy.return
}
}
```
```mlir
module {
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00], [2.000000e+00]]> \
- : tensor<2x1xf64>} : () -> tensor<2x1xf64>
- "toy.print"(%0) : (tensor<2x1xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00], [2.000000e+00]]> : tensor<2x1xf64>
+ toy.print %0 : tensor<2x1xf64>
+ toy.return
}
}
```
```mlir
func @multiply_transpose(%arg0: tensor<*xf64>, %arg1: tensor<*xf64>) -> tensor<*xf64> {
- %0 = "toy.transpose"(%arg0) : (tensor<*xf64>) -> tensor<*xf64>
- %1 = "toy.transpose"(%arg1) : (tensor<*xf64>) -> tensor<*xf64>
- %2 = "toy.mul"(%0, %1) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
- "toy.return"(%2) : (tensor<*xf64>) -> ()
+ %0 = toy.transpose(%arg0 : tensor<*xf64>) to tensor<*xf64>
+ %1 = toy.transpose(%arg1 : tensor<*xf64>) to tensor<*xf64>
+ %2 = toy.mul %0, %1 : tensor<*xf64>
+ toy.return %2 : tensor<*xf64>
}
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %1 = "toy.reshape"(%0) : (tensor<2x3xf64>) -> tensor<2x3xf64>
- %2 = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
- %3 = "toy.reshape"(%2) : (tensor<6xf64>) -> tensor<2x3xf64>
- %4 = "toy.generic_call"(%1, %3) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
- %5 = "toy.generic_call"(%3, %1) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
- "toy.print"(%5) : (tensor<*xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %1 = toy.reshape(%0 : tensor<2x3xf64>) to tensor<2x3xf64>
+ %2 = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+ %3 = toy.reshape(%2 : tensor<6xf64>) to tensor<2x3xf64>
+ %4 = toy.generic_call @multiply_transpose(%1, %3) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+ %5 = toy.generic_call @multiply_transpose(%3, %1) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+ toy.print %5 : tensor<*xf64>
+ toy.return
}
```
%4 = "toy.transpose"(%2) : (tensor<*xf64>) -> tensor<*xf64>
%5 = "toy.transpose"(%3) : (tensor<*xf64>) -> tensor<*xf64>
%6 = "toy.mul"(%4, %5) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
- "toy.print"(%6) : (tensor<*xf64>) -> ()
- "toy.return"() : () -> ()
+ toy.print %6 : tensor<*xf64>
+ toy.return
}
```
%0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
%1 = "toy.transpose"(%0) : (tensor<2x3xf64>) -> tensor<3x2xf64>
%2 = "toy.mul"(%1, %1) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
- "toy.print"(%2) : (tensor<3x2xf64>) -> ()
- "toy.return"() : () -> ()
+ toy.print %2 : tensor<3x2xf64>
+ toy.return
}
```
```mlir
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %2 = "toy.transpose"(%0) : (tensor<2x3xf64>) -> tensor<3x2xf64>
- %3 = "toy.mul"(%2, %2) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
- "toy.print"(%3) : (tensor<3x2xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %2 = toy.transpose(%0 : tensor<2x3xf64>) to tensor<3x2xf64>
+ %3 = toy.mul %2, %2 : tensor<3x2xf64>
+ toy.print %3 : tensor<3x2xf64>
+ toy.return
}
```
}
// Print the value held by the buffer.
- "toy.print"(%0) : (memref<3x2xf64>) -> ()
+ toy.print %0 : memref<3x2xf64>
dealloc %2 : memref<2x3xf64>
dealloc %1 : memref<3x2xf64>
dealloc %0 : memref<3x2xf64>
}
// Print the value held by the buffer.
- "toy.print"(%0) : (memref<3x2xf64>) -> ()
+ toy.print %0 : memref<3x2xf64>
dealloc %1 : memref<2x3xf64>
dealloc %0 : memref<3x2xf64>
return
```mlir
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %2 = "toy.transpose"(%0) : (tensor<2x3xf64>) -> tensor<3x2xf64>
- %3 = "toy.mul"(%2, %2) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
- "toy.print"(%3) : (tensor<3x2xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %2 = toy.transpose(%0 : tensor<2x3xf64>) to tensor<3x2xf64>
+ %3 = toy.mul %2, %2 : tensor<3x2xf64>
+ toy.print %3 : tensor<3x2xf64>
+ toy.return
}
```
```mlir
module {
func @multiply_transpose(%arg0: !toy.struct<tensor<*xf64>, tensor<*xf64>>) {
- "toy.return"() : () -> ()
+ toy.return
}
}
```
that contains a set of constant values for each of the `struct` elements.
```mlir
- %0 = "toy.struct_constant"() {
- value = [dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64>]
- } : () -> !toy.struct<tensor<*xf64>>
+ %0 = toy.struct_constant [
+ dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64>
+ ] : !toy.struct<tensor<*xf64>>
```
##### `toy.struct_access`
This new operation materializes the Nth element of a `struct` value.
```mlir
- %0 = "toy.struct_constant"() {
- value = [dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64>]
- } : () -> !toy.struct<tensor<*xf64>>
- %1 = "toy.struct_access"(%0) {index = 0 : i64} : (!toy.struct<tensor<*xf64>>) -> tensor<*xf64>
+ %0 = toy.struct_constant [
+ dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64>
+ ] : !toy.struct<tensor<*xf64>>
+ %1 = toy.struct_access %0[0] : !toy.struct<tensor<*xf64>> -> tensor<*xf64>
```
With these operations, we can revisit our original example:
```mlir
module {
func @multiply_transpose(%arg0: !toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64> {
- %0 = "toy.struct_access"(%arg0) {index = 0 : i64} : (!toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
- %1 = "toy.transpose"(%0) : (tensor<*xf64>) -> tensor<*xf64>
- %2 = "toy.struct_access"(%arg0) {index = 1 : i64} : (!toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
- %3 = "toy.transpose"(%2) : (tensor<*xf64>) -> tensor<*xf64>
- %4 = "toy.mul"(%1, %3) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
- "toy.return"(%4) : (tensor<*xf64>) -> ()
+ %0 = toy.struct_access %arg0[0] : !toy.struct<tensor<*xf64>, tensor<*xf64>> -> tensor<*xf64>
+ %1 = toy.transpose(%0 : tensor<*xf64>) to tensor<*xf64>
+ %2 = toy.struct_access %arg0[1] : !toy.struct<tensor<*xf64>, tensor<*xf64>> -> tensor<*xf64>
+ %3 = toy.transpose(%2 : tensor<*xf64>) to tensor<*xf64>
+ %4 = toy.mul %1, %3 : tensor<*xf64>
+ toy.return %4 : tensor<*xf64>
}
func @main() {
- %0 = "toy.struct_constant"() {value = [dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>, dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>]} : () -> !toy.struct<tensor<*xf64>, tensor<*xf64>>
- %1 = "toy.generic_call"(%0) {callee = @multiply_transpose} : (!toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
- "toy.print"(%1) : (tensor<*xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.struct_constant [
+ dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>,
+ dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ ] : !toy.struct<tensor<*xf64>, tensor<*xf64>>
+ %1 = toy.generic_call @multiply_transpose(%0) : (!toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
+ toy.print %1 : tensor<*xf64>
+ toy.return
}
}
```
```mlir
module {
func @main() {
- %0 = "toy.struct_constant"() {value = [dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>, dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>]} : () -> !toy.struct<tensor<*xf64>, tensor<*xf64>>
- %1 = "toy.struct_access"(%0) {index = 0 : i64} : (!toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
- %2 = "toy.transpose"(%1) : (tensor<*xf64>) -> tensor<*xf64>
- %3 = "toy.struct_access"(%0) {index = 1 : i64} : (!toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
- %4 = "toy.transpose"(%3) : (tensor<*xf64>) -> tensor<*xf64>
- %5 = "toy.mul"(%2, %4) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
- "toy.print"(%5) : (tensor<*xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.struct_constant [
+ dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>,
+ dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ ] : !toy.struct<tensor<*xf64>, tensor<*xf64>>
+ %1 = toy.struct_access %0[0] : !toy.struct<tensor<*xf64>, tensor<*xf64>> -> tensor<*xf64>
+ %2 = toy.transpose(%1 : tensor<*xf64>) to tensor<*xf64>
+ %3 = toy.struct_access %0[1] : !toy.struct<tensor<*xf64>, tensor<*xf64>> -> tensor<*xf64>
+ %4 = toy.transpose(%3 : tensor<*xf64>) to tensor<*xf64>
+ %5 = toy.mul %2, %4 : tensor<*xf64>
+ toy.print %5 : tensor<*xf64>
+ toy.return
}
}
```
```mlir
module {
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %1 = "toy.transpose"(%0) : (tensor<2x3xf64>) -> tensor<3x2xf64>
- %2 = "toy.mul"(%1, %1) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
- "toy.print"(%2) : (tensor<3x2xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %1 = toy.transpose(%0 : tensor<2x3xf64>) to tensor<3x2xf64>
+ %2 = toy.mul %1, %1 : tensor<3x2xf64>
+ toy.print %2 : tensor<3x2xf64>
+ toy.return
}
}
```
to the operation as an attribute. For example:
```mlir
- %0 = "toy.constant"()
- { value = dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64> }
- : () -> tensor<2x3xf64>
+ %0 = toy.constant dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]>
+ : tensor<2x3xf64>
```
}];
// The constant operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseConstantOp(parser, result); }];
+ let printer = [{ return ::print(p, *this); }];
+
// Add custom build methods for the constant operation. These method populates
// the `state` that MLIR uses to create operations, i.e. these are used when
// using `builder.create<ConstantOp>(...)`.
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building an AddOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
arguments expected by the callee. For example:
```mlir
- %4 = "toy.generic_call"(%1, %3) {callee = @my_func}
+ %4 = toy.generic_call @my_func(%1, %3)
: (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
```
// The generic call operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = [{
+ $callee `(` $inputs `)` attr-dict `:` functional-type($inputs, results)
+ }];
+
// Add custom build methods for the generic call operation.
let builders = [
OpBuilder<"Builder *builder, OperationState &state, "
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building a MulOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
// The print operation takes an input tensor to print.
let arguments = (ins F64Tensor:$input);
+
+ let assemblyFormat = "$input attr-dict `:` type($input)";
}
def ReshapeOp : Toy_Op<"reshape"> {
the same number of elements but different shapes. For example:
```mlir
- %0 = "toy.reshape"(%arg1) : (tensor<10xf64>) -> tensor<5x2xf64>
+ %0 = toy.reshape (%arg1 : tensor<10xf64>) to tensor<5x2xf64>
```
}];
// We expect that the reshape operation returns a statically shaped tensor.
let results = (outs StaticShapeTensorOf<[F64]>);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
}
def ReturnOp : Toy_Op<"return", [Terminator, HasParent<"FuncOp">]> {
// value must match the return type of the enclosing function.
let arguments = (ins Variadic<F64Tensor>:$input);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = "($input^ `:` type($input))? attr-dict ";
+
// Allow building a ReturnOp with no return operand.
let builders = [OpBuilder<
"Builder *b, OperationState &state", [{ build(b, state, llvm::None); }]
let arguments = (ins F64Tensor:$input);
let results = (outs F64Tensor);
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
// Allow building a TransposeOp with from the input operand.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value input">
#include "toy/Dialect.h"
#include "mlir/IR/Builders.h"
+#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/StandardTypes.h"
using namespace mlir;
// Toy Operations
//===----------------------------------------------------------------------===//
+/// A generalized parser for binary operations. This parses the different forms
+/// of 'printBinaryOp' below.
+static mlir::ParseResult parseBinaryOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ SmallVector<mlir::OpAsmParser::OperandType, 2> operands;
+ llvm::SMLoc operandsLoc = parser.getCurrentLocation();
+ Type type;
+ if (parser.parseOperandList(operands, /*requiredOperandCount=*/2) ||
+ parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseColonType(type))
+ return mlir::failure();
+
+ // If the type is a function type, it contains the input and result types of
+ // this operation.
+ if (FunctionType funcType = type.dyn_cast<FunctionType>()) {
+ if (parser.resolveOperands(operands, funcType.getInputs(), operandsLoc,
+ result.operands))
+ return mlir::failure();
+ result.addTypes(funcType.getResults());
+ return mlir::success();
+ }
+
+ // Otherwise, the parsed type is the type of both operands and results.
+ if (parser.resolveOperands(operands, type, result.operands))
+ return mlir::failure();
+ result.addTypes(type);
+ return mlir::success();
+}
+
+/// A generalized printer for binary operations. It prints in two different
+/// forms depending on if all of the types match.
+static void printBinaryOp(mlir::OpAsmPrinter &printer, mlir::Operation *op) {
+ printer << op->getName() << " " << op->getOperands();
+ printer.printOptionalAttrDict(op->getAttrs());
+ printer << " : ";
+
+ // If all of the types are the same, print the type directly.
+ Type resultType = *op->result_type_begin();
+ if (llvm::all_of(op->getOperandTypes(),
+ [=](Type type) { return type == resultType; })) {
+ printer << resultType;
+ return;
+ }
+
+ // Otherwise, print a functional type.
+ printer.printFunctionalType(op->getOperandTypes(), op->getResultTypes());
+}
+
//===----------------------------------------------------------------------===//
// ConstantOp
ConstantOp::build(builder, state, dataType, dataAttribute);
}
+/// The 'OpAsmPrinter' class provides a collection of methods for parsing
+/// various punctuation, as well as attributes, operands, types, etc. Each of
+/// these methods returns a `ParseResult`. This class is a wrapper around
+/// `LogicalResult` that can be converted to a boolean `true` value on failure,
+/// or `false` on success. This allows for easily chaining together a set of
+/// parser rules. These rules are used to populate an `mlir::OperationState`
+/// similarly to the `build` methods described above.
+static mlir::ParseResult parseConstantOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ mlir::DenseElementsAttr value;
+ if (parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseAttribute(value, "value", result.attributes))
+ return failure();
+
+ result.addTypes(value.getType());
+ return success();
+}
+
+/// The 'OpAsmPrinter' class is a stream that will allows for formatting
+/// strings, attributes, operands, types, etc.
+static void print(mlir::OpAsmPrinter &printer, ConstantOp op) {
+ printer << "toy.constant ";
+ printer.printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"value"});
+ printer << op.value();
+}
+
/// Verifier for the constant operation. This corresponds to the `::verify(...)`
/// in the op definition.
static mlir::LogicalResult verify(ConstantOp op) {
to the operation as an attribute. For example:
```mlir
- %0 = "toy.constant"()
- { value = dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64> }
- : () -> tensor<2x3xf64>
+ %0 = toy.constant dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]>
+ : tensor<2x3xf64>
```
}];
// The constant operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseConstantOp(parser, result); }];
+ let printer = [{ return ::print(p, *this); }];
+
// Add custom build methods for the constant operation. These method populates
// the `state` that MLIR uses to create operations, i.e. these are used when
// using `builder.create<ConstantOp>(...)`.
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building an AddOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
arguments expected by the callee. For example:
```mlir
- %4 = "toy.generic_call"(%1, %3) {callee = @my_func}
+ %4 = toy.generic_call @my_func(%1, %3)
: (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
```
// The generic call operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = [{
+ $callee `(` $inputs `)` attr-dict `:` functional-type($inputs, results)
+ }];
+
// Add custom build methods for the generic call operation.
let builders = [
OpBuilder<"Builder *builder, OperationState &state, "
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building a MulOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
// The print operation takes an input tensor to print.
let arguments = (ins F64Tensor:$input);
+
+ let assemblyFormat = "$input attr-dict `:` type($input)";
}
def ReshapeOp : Toy_Op<"reshape", [NoSideEffect]> {
the same number of elements but different shapes. For example:
```mlir
- %0 = "toy.reshape"(%arg1) : (tensor<10xf64>) -> tensor<5x2xf64>
+ %0 = toy.reshape (%arg1 : tensor<10xf64>) to tensor<5x2xf64>
```
}];
let arguments = (ins F64Tensor:$input);
- // Enabled registering canonicalization patterns with this operation.
- let hasCanonicalizer = 1;
-
// We expect that the reshape operation returns a statically shaped tensor.
let results = (outs StaticShapeTensorOf<[F64]>);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
+ // Enable registering canonicalization patterns with this operation.
+ let hasCanonicalizer = 1;
}
def ReturnOp : Toy_Op<"return", [Terminator, HasParent<"FuncOp">]> {
// value must match the return type of the enclosing function.
let arguments = (ins Variadic<F64Tensor>:$input);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = "($input^ `:` type($input))? attr-dict ";
+
// Allow building a ReturnOp with no return operand.
let builders = [OpBuilder<
"Builder *b, OperationState &state", [{ build(b, state, llvm::None); }]
let arguments = (ins F64Tensor:$input);
let results = (outs F64Tensor);
- // Enabled registering canonicalization patterns with this operation.
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
+ // Enable registering canonicalization patterns with this operation.
let hasCanonicalizer = 1;
// Allow building a TransposeOp with from the input operand.
#include "toy/Dialect.h"
#include "mlir/IR/Builders.h"
+#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/StandardTypes.h"
using namespace mlir;
// Toy Operations
//===----------------------------------------------------------------------===//
+/// A generalized parser for binary operations. This parses the different forms
+/// of 'printBinaryOp' below.
+static mlir::ParseResult parseBinaryOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ SmallVector<mlir::OpAsmParser::OperandType, 2> operands;
+ llvm::SMLoc operandsLoc = parser.getCurrentLocation();
+ Type type;
+ if (parser.parseOperandList(operands, /*requiredOperandCount=*/2) ||
+ parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseColonType(type))
+ return mlir::failure();
+
+ // If the type is a function type, it contains the input and result types of
+ // this operation.
+ if (FunctionType funcType = type.dyn_cast<FunctionType>()) {
+ if (parser.resolveOperands(operands, funcType.getInputs(), operandsLoc,
+ result.operands))
+ return mlir::failure();
+ result.addTypes(funcType.getResults());
+ return mlir::success();
+ }
+
+ // Otherwise, the parsed type is the type of both operands and results.
+ if (parser.resolveOperands(operands, type, result.operands))
+ return mlir::failure();
+ result.addTypes(type);
+ return mlir::success();
+}
+
+/// A generalized printer for binary operations. It prints in two different
+/// forms depending on if all of the types match.
+static void printBinaryOp(mlir::OpAsmPrinter &printer, mlir::Operation *op) {
+ printer << op->getName() << " " << op->getOperands();
+ printer.printOptionalAttrDict(op->getAttrs());
+ printer << " : ";
+
+ // If all of the types are the same, print the type directly.
+ Type resultType = *op->result_type_begin();
+ if (llvm::all_of(op->getOperandTypes(),
+ [=](Type type) { return type == resultType; })) {
+ printer << resultType;
+ return;
+ }
+
+ // Otherwise, print a functional type.
+ printer.printFunctionalType(op->getOperandTypes(), op->getResultTypes());
+}
+
//===----------------------------------------------------------------------===//
// ConstantOp
ConstantOp::build(builder, state, dataType, dataAttribute);
}
+/// The 'OpAsmPrinter' class provides a collection of methods for parsing
+/// various punctuation, as well as attributes, operands, types, etc. Each of
+/// these methods returns a `ParseResult`. This class is a wrapper around
+/// `LogicalResult` that can be converted to a boolean `true` value on failure,
+/// or `false` on success. This allows for easily chaining together a set of
+/// parser rules. These rules are used to populate an `mlir::OperationState`
+/// similarly to the `build` methods described above.
+static mlir::ParseResult parseConstantOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ mlir::DenseElementsAttr value;
+ if (parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseAttribute(value, "value", result.attributes))
+ return failure();
+
+ result.addTypes(value.getType());
+ return success();
+}
+
+/// The 'OpAsmPrinter' class is a stream that will allows for formatting
+/// strings, attributes, operands, types, etc.
+static void print(mlir::OpAsmPrinter &printer, ConstantOp op) {
+ printer << "toy.constant ";
+ printer.printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"value"});
+ printer << op.value();
+}
+
/// Verifier for the constant operation. This corresponds to the `::verify(...)`
/// in the op definition.
static mlir::LogicalResult verify(ConstantOp op) {
to the operation as an attribute. For example:
```mlir
- %0 = "toy.constant"()
- { value = dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64> }
- : () -> tensor<2x3xf64>
+ %0 = toy.constant dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]>
+ : tensor<2x3xf64>
```
}];
// The constant operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseConstantOp(parser, result); }];
+ let printer = [{ return ::print(p, *this); }];
+
// Add custom build methods for the constant operation. These method populates
// the `state` that MLIR uses to create operations, i.e. these are used when
// using `builder.create<ConstantOp>(...)`.
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building an AddOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
let arguments = (ins F64Tensor:$input);
let results = (outs F64Tensor:$output);
+ let assemblyFormat = "$input attr-dict `:` type($input) `to` type($output)";
+
// Set the folder bit so that we can fold redundant cast operations.
let hasFolder = 1;
}
arguments expected by the callee. For example:
```mlir
- %4 = "toy.generic_call"(%1, %3) {callee = @my_func}
+ %4 = toy.generic_call @my_func(%1, %3)
: (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
```
// The generic call operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = [{
+ $callee `(` $inputs `)` attr-dict `:` functional-type($inputs, results)
+ }];
+
// Add custom build methods for the generic call operation.
let builders = [
OpBuilder<"Builder *builder, OperationState &state, "
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building a MulOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
// The print operation takes an input tensor to print.
let arguments = (ins F64Tensor:$input);
+
+ let assemblyFormat = "$input attr-dict `:` type($input)";
}
def ReshapeOp : Toy_Op<"reshape", [NoSideEffect]> {
the same number of elements but different shapes. For example:
```mlir
- %0 = "toy.reshape"(%arg1) : (tensor<10xf64>) -> tensor<5x2xf64>
+ %0 = toy.reshape (%arg1 : tensor<10xf64>) to tensor<5x2xf64>
```
}];
let arguments = (ins F64Tensor:$input);
- let hasCanonicalizer = 1;
// We expect that the reshape operation returns a statically shaped tensor.
let results = (outs StaticShapeTensorOf<[F64]>);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
+ // Enable registering canonicalization patterns with this operation.
+ let hasCanonicalizer = 1;
}
def ReturnOp : Toy_Op<"return", [Terminator, HasParent<"FuncOp">]> {
// value must match the return type of the enclosing function.
let arguments = (ins Variadic<F64Tensor>:$input);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = "($input^ `:` type($input))? attr-dict ";
+
// Allow building a ReturnOp with no return operand.
let builders = [OpBuilder<
"Builder *b, OperationState &state", [{ build(b, state, llvm::None); }]
let arguments = (ins F64Tensor:$input);
let results = (outs F64Tensor);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
+ // Enable registering canonicalization patterns with this operation.
let hasCanonicalizer = 1;
// Allow building a TransposeOp with from the input operand.
#include "toy/Dialect.h"
#include "mlir/IR/Builders.h"
+#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/Transforms/InliningUtils.h"
// Toy Operations
//===----------------------------------------------------------------------===//
+/// A generalized parser for binary operations. This parses the different forms
+/// of 'printBinaryOp' below.
+static mlir::ParseResult parseBinaryOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ SmallVector<mlir::OpAsmParser::OperandType, 2> operands;
+ llvm::SMLoc operandsLoc = parser.getCurrentLocation();
+ Type type;
+ if (parser.parseOperandList(operands, /*requiredOperandCount=*/2) ||
+ parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseColonType(type))
+ return mlir::failure();
+
+ // If the type is a function type, it contains the input and result types of
+ // this operation.
+ if (FunctionType funcType = type.dyn_cast<FunctionType>()) {
+ if (parser.resolveOperands(operands, funcType.getInputs(), operandsLoc,
+ result.operands))
+ return mlir::failure();
+ result.addTypes(funcType.getResults());
+ return mlir::success();
+ }
+
+ // Otherwise, the parsed type is the type of both operands and results.
+ if (parser.resolveOperands(operands, type, result.operands))
+ return mlir::failure();
+ result.addTypes(type);
+ return mlir::success();
+}
+
+/// A generalized printer for binary operations. It prints in two different
+/// forms depending on if all of the types match.
+static void printBinaryOp(mlir::OpAsmPrinter &printer, mlir::Operation *op) {
+ printer << op->getName() << " " << op->getOperands();
+ printer.printOptionalAttrDict(op->getAttrs());
+ printer << " : ";
+
+ // If all of the types are the same, print the type directly.
+ Type resultType = *op->result_type_begin();
+ if (llvm::all_of(op->getOperandTypes(),
+ [=](Type type) { return type == resultType; })) {
+ printer << resultType;
+ return;
+ }
+
+ // Otherwise, print a functional type.
+ printer.printFunctionalType(op->getOperandTypes(), op->getResultTypes());
+}
+
//===----------------------------------------------------------------------===//
// ConstantOp
ConstantOp::build(builder, state, dataType, dataAttribute);
}
+/// The 'OpAsmPrinter' class provides a collection of methods for parsing
+/// various punctuation, as well as attributes, operands, types, etc. Each of
+/// these methods returns a `ParseResult`. This class is a wrapper around
+/// `LogicalResult` that can be converted to a boolean `true` value on failure,
+/// or `false` on success. This allows for easily chaining together a set of
+/// parser rules. These rules are used to populate an `mlir::OperationState`
+/// similarly to the `build` methods described above.
+static mlir::ParseResult parseConstantOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ mlir::DenseElementsAttr value;
+ if (parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseAttribute(value, "value", result.attributes))
+ return failure();
+
+ result.addTypes(value.getType());
+ return success();
+}
+
+/// The 'OpAsmPrinter' class is a stream that will allows for formatting
+/// strings, attributes, operands, types, etc.
+static void print(mlir::OpAsmPrinter &printer, ConstantOp op) {
+ printer << "toy.constant ";
+ printer.printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"value"});
+ printer << op.value();
+}
+
/// Verifier for the constant operation. This corresponds to the `::verify(...)`
/// in the op definition.
static mlir::LogicalResult verify(ConstantOp op) {
to the operation as an attribute. For example:
```mlir
- %0 = "toy.constant"()
- { value = dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64> }
- : () -> tensor<2x3xf64>
+ %0 = toy.constant dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]>
+ : tensor<2x3xf64>
```
}];
// The constant operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseConstantOp(parser, result); }];
+ let printer = [{ return ::print(p, *this); }];
+
// Add custom build methods for the constant operation. These method populates
// the `state` that MLIR uses to create operations, i.e. these are used when
// using `builder.create<ConstantOp>(...)`.
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building an AddOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
let arguments = (ins F64Tensor:$input);
let results = (outs F64Tensor:$output);
+ let assemblyFormat = "$input attr-dict `:` type($input) `to` type($output)";
+
// Set the folder bit so that we can fold redundant cast operations.
let hasFolder = 1;
}
arguments expected by the callee. For example:
```mlir
- %4 = "toy.generic_call"(%1, %3) {callee = @my_func}
+ %4 = toy.generic_call @my_func(%1, %3)
: (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
```
// The generic call operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = [{
+ $callee `(` $inputs `)` attr-dict `:` functional-type($inputs, results)
+ }];
+
// Add custom build methods for the generic call operation.
let builders = [
OpBuilder<"Builder *builder, OperationState &state, "
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building a MulOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
// The print operation takes an input tensor to print.
// We also allow a F64MemRef to enable interop during partial lowering.
let arguments = (ins AnyTypeOf<[F64Tensor, F64MemRef]>:$input);
+
+ let assemblyFormat = "$input attr-dict `:` type($input)";
}
def ReshapeOp : Toy_Op<"reshape", [NoSideEffect]> {
the same number of elements but different shapes. For example:
```mlir
- %0 = "toy.reshape"(%arg1) : (tensor<10xf64>) -> tensor<5x2xf64>
+ %0 = toy.reshape (%arg1 : tensor<10xf64>) to tensor<5x2xf64>
```
}];
let arguments = (ins F64Tensor:$input);
- let hasCanonicalizer = 1;
// We expect that the reshape operation returns a statically shaped tensor.
let results = (outs StaticShapeTensorOf<[F64]>);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
+ // Enable registering canonicalization patterns with this operation.
+ let hasCanonicalizer = 1;
}
def ReturnOp : Toy_Op<"return", [Terminator, HasParent<"FuncOp">]> {
// value must match the return type of the enclosing function.
let arguments = (ins Variadic<F64Tensor>:$input);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = "($input^ `:` type($input))? attr-dict ";
+
// Allow building a ReturnOp with no return operand.
let builders = [OpBuilder<
"Builder *b, OperationState &state", [{ build(b, state, llvm::None); }]
let arguments = (ins F64Tensor:$input);
let results = (outs F64Tensor);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
+ // Enable registering canonicalization patterns with this operation.
let hasCanonicalizer = 1;
// Allow building a TransposeOp with from the input operand.
#include "toy/Dialect.h"
#include "mlir/IR/Builders.h"
+#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/Transforms/InliningUtils.h"
// Toy Operations
//===----------------------------------------------------------------------===//
+/// A generalized parser for binary operations. This parses the different forms
+/// of 'printBinaryOp' below.
+static mlir::ParseResult parseBinaryOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ SmallVector<mlir::OpAsmParser::OperandType, 2> operands;
+ llvm::SMLoc operandsLoc = parser.getCurrentLocation();
+ Type type;
+ if (parser.parseOperandList(operands, /*requiredOperandCount=*/2) ||
+ parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseColonType(type))
+ return mlir::failure();
+
+ // If the type is a function type, it contains the input and result types of
+ // this operation.
+ if (FunctionType funcType = type.dyn_cast<FunctionType>()) {
+ if (parser.resolveOperands(operands, funcType.getInputs(), operandsLoc,
+ result.operands))
+ return mlir::failure();
+ result.addTypes(funcType.getResults());
+ return mlir::success();
+ }
+
+ // Otherwise, the parsed type is the type of both operands and results.
+ if (parser.resolveOperands(operands, type, result.operands))
+ return mlir::failure();
+ result.addTypes(type);
+ return mlir::success();
+}
+
+/// A generalized printer for binary operations. It prints in two different
+/// forms depending on if all of the types match.
+static void printBinaryOp(mlir::OpAsmPrinter &printer, mlir::Operation *op) {
+ printer << op->getName() << " " << op->getOperands();
+ printer.printOptionalAttrDict(op->getAttrs());
+ printer << " : ";
+
+ // If all of the types are the same, print the type directly.
+ Type resultType = *op->result_type_begin();
+ if (llvm::all_of(op->getOperandTypes(),
+ [=](Type type) { return type == resultType; })) {
+ printer << resultType;
+ return;
+ }
+
+ // Otherwise, print a functional type.
+ printer.printFunctionalType(op->getOperandTypes(), op->getResultTypes());
+}
+
//===----------------------------------------------------------------------===//
// ConstantOp
ConstantOp::build(builder, state, dataType, dataAttribute);
}
+/// The 'OpAsmPrinter' class provides a collection of methods for parsing
+/// various punctuation, as well as attributes, operands, types, etc. Each of
+/// these methods returns a `ParseResult`. This class is a wrapper around
+/// `LogicalResult` that can be converted to a boolean `true` value on failure,
+/// or `false` on success. This allows for easily chaining together a set of
+/// parser rules. These rules are used to populate an `mlir::OperationState`
+/// similarly to the `build` methods described above.
+static mlir::ParseResult parseConstantOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ mlir::DenseElementsAttr value;
+ if (parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseAttribute(value, "value", result.attributes))
+ return failure();
+
+ result.addTypes(value.getType());
+ return success();
+}
+
+/// The 'OpAsmPrinter' class is a stream that will allows for formatting
+/// strings, attributes, operands, types, etc.
+static void print(mlir::OpAsmPrinter &printer, ConstantOp op) {
+ printer << "toy.constant ";
+ printer.printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"value"});
+ printer << op.value();
+}
+
/// Verifier for the constant operation. This corresponds to the `::verify(...)`
/// in the op definition.
static mlir::LogicalResult verify(ConstantOp op) {
to the operation as an attribute. For example:
```mlir
- %0 = "toy.constant"()
- { value = dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64> }
- : () -> tensor<2x3xf64>
+ %0 = toy.constant dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]>
+ : tensor<2x3xf64>
```
}];
// The constant operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseConstantOp(parser, result); }];
+ let printer = [{ return ::print(p, *this); }];
+
// Add custom build methods for the constant operation. These method populates
// the `state` that MLIR uses to create operations, i.e. these are used when
// using `builder.create<ConstantOp>(...)`.
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building an AddOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
let arguments = (ins F64Tensor:$input);
let results = (outs F64Tensor:$output);
+ let assemblyFormat = "$input attr-dict `:` type($input) `to` type($output)";
+
// Set the folder bit so that we can fold redundant cast operations.
let hasFolder = 1;
}
arguments expected by the callee. For example:
```mlir
- %4 = "toy.generic_call"(%1, %3) {callee = @my_func}
+ %4 = toy.generic_call @my_func(%1, %3)
: (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
```
// The generic call operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = [{
+ $callee `(` $inputs `)` attr-dict `:` functional-type($inputs, results)
+ }];
+
// Add custom build methods for the generic call operation.
let builders = [
OpBuilder<"Builder *builder, OperationState &state, "
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building a MulOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
// The print operation takes an input tensor to print.
// We also allow a F64MemRef to enable interop during partial lowering.
let arguments = (ins AnyTypeOf<[F64Tensor, F64MemRef]>:$input);
+
+ let assemblyFormat = "$input attr-dict `:` type($input)";
}
def ReshapeOp : Toy_Op<"reshape", [NoSideEffect]> {
the same number of elements but different shapes. For example:
```mlir
- %0 = "toy.reshape"(%arg1) : (tensor<10xf64>) -> tensor<5x2xf64>
+ %0 = toy.reshape (%arg1 : tensor<10xf64>) to tensor<5x2xf64>
```
}];
let arguments = (ins F64Tensor:$input);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
+ // Enable registering canonicalization patterns with this operation.
let hasCanonicalizer = 1;
// We expect that the reshape operation returns a statically shaped tensor.
// value must match the return type of the enclosing function.
let arguments = (ins Variadic<F64Tensor>:$input);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = "($input^ `:` type($input))? attr-dict ";
+
// Allow building a ReturnOp with no return operand.
let builders = [OpBuilder<
"Builder *b, OperationState &state", [{ build(b, state, llvm::None); }]
let arguments = (ins F64Tensor:$input);
let results = (outs F64Tensor);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
+ // Enable registering canonicalization patterns with this operation.
let hasCanonicalizer = 1;
// Allow building a TransposeOp with from the input operand.
#include "toy/Dialect.h"
#include "mlir/IR/Builders.h"
+#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/Transforms/InliningUtils.h"
// Toy Operations
//===----------------------------------------------------------------------===//
+/// A generalized parser for binary operations. This parses the different forms
+/// of 'printBinaryOp' below.
+static mlir::ParseResult parseBinaryOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ SmallVector<mlir::OpAsmParser::OperandType, 2> operands;
+ llvm::SMLoc operandsLoc = parser.getCurrentLocation();
+ Type type;
+ if (parser.parseOperandList(operands, /*requiredOperandCount=*/2) ||
+ parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseColonType(type))
+ return mlir::failure();
+
+ // If the type is a function type, it contains the input and result types of
+ // this operation.
+ if (FunctionType funcType = type.dyn_cast<FunctionType>()) {
+ if (parser.resolveOperands(operands, funcType.getInputs(), operandsLoc,
+ result.operands))
+ return mlir::failure();
+ result.addTypes(funcType.getResults());
+ return mlir::success();
+ }
+
+ // Otherwise, the parsed type is the type of both operands and results.
+ if (parser.resolveOperands(operands, type, result.operands))
+ return mlir::failure();
+ result.addTypes(type);
+ return mlir::success();
+}
+
+/// A generalized printer for binary operations. It prints in two different
+/// forms depending on if all of the types match.
+static void printBinaryOp(mlir::OpAsmPrinter &printer, mlir::Operation *op) {
+ printer << op->getName() << " " << op->getOperands();
+ printer.printOptionalAttrDict(op->getAttrs());
+ printer << " : ";
+
+ // If all of the types are the same, print the type directly.
+ Type resultType = *op->result_type_begin();
+ if (llvm::all_of(op->getOperandTypes(),
+ [=](Type type) { return type == resultType; })) {
+ printer << resultType;
+ return;
+ }
+
+ // Otherwise, print a functional type.
+ printer.printFunctionalType(op->getOperandTypes(), op->getResultTypes());
+}
+
//===----------------------------------------------------------------------===//
// ConstantOp
ConstantOp::build(builder, state, dataType, dataAttribute);
}
+/// The 'OpAsmPrinter' class provides a collection of methods for parsing
+/// various punctuation, as well as attributes, operands, types, etc. Each of
+/// these methods returns a `ParseResult`. This class is a wrapper around
+/// `LogicalResult` that can be converted to a boolean `true` value on failure,
+/// or `false` on success. This allows for easily chaining together a set of
+/// parser rules. These rules are used to populate an `mlir::OperationState`
+/// similarly to the `build` methods described above.
+static mlir::ParseResult parseConstantOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ mlir::DenseElementsAttr value;
+ if (parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseAttribute(value, "value", result.attributes))
+ return failure();
+
+ result.addTypes(value.getType());
+ return success();
+}
+
+/// The 'OpAsmPrinter' class is a stream that will allows for formatting
+/// strings, attributes, operands, types, etc.
+static void print(mlir::OpAsmPrinter &printer, ConstantOp op) {
+ printer << "toy.constant ";
+ printer.printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"value"});
+ printer << op.value();
+}
+
/// Verifier for the constant operation. This corresponds to the `::verify(...)`
/// in the op definition.
static mlir::LogicalResult verify(ConstantOp op) {
to the operation as an attribute. For example:
```mlir
- %0 = "toy.constant"()
- { value = dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64> }
- : () -> tensor<2x3xf64>
+ %0 = toy.constant dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]>
+ : tensor<2x3xf64>
```
}];
// The constant operation returns a single value of TensorType.
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseConstantOp(parser, result); }];
+ let printer = [{ return ::print(p, *this); }];
+
// Add custom build methods for the constant operation. These method populates
// the `state` that MLIR uses to create operations, i.e. these are used when
// using `builder.create<ConstantOp>(...)`.
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building an AddOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
let arguments = (ins F64Tensor:$input);
let results = (outs F64Tensor:$output);
+ let assemblyFormat = "$input attr-dict `:` type($input) `to` type($output)";
+
// Set the folder bit so that we can fold redundant cast operations.
let hasFolder = 1;
}
arguments expected by the callee. For example:
```mlir
- %4 = "toy.generic_call"(%1, %3) {callee = @my_func}
+ %4 = toy.generic_call @my_func(%1, %3)
: (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
```
// StructType.
let results = (outs Toy_Type);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = [{
+ $callee `(` $inputs `)` attr-dict `:` functional-type($inputs, results)
+ }];
+
// Add custom build methods for the generic call operation.
let builders = [
OpBuilder<"Builder *builder, OperationState &state, "
let arguments = (ins F64Tensor:$lhs, F64Tensor:$rhs);
let results = (outs F64Tensor);
+ // Specify a parser and printer method.
+ let parser = [{ return ::parseBinaryOp(parser, result); }];
+ let printer = [{ return ::printBinaryOp(p, *this); }];
+
// Allow building a MulOp with from the two input operands.
let builders = [
OpBuilder<"Builder *b, OperationState &state, Value lhs, Value rhs">
// The print operation takes an input tensor to print.
// We also allow a F64MemRef to enable interop during partial lowering.
let arguments = (ins AnyTypeOf<[F64Tensor, F64MemRef]>:$input);
+
+ let assemblyFormat = "$input attr-dict `:` type($input)";
}
def ReshapeOp : Toy_Op<"reshape", [NoSideEffect]> {
the same number of elements but different shapes. For example:
```mlir
- %0 = "toy.reshape"(%arg1) : (tensor<10xf64>) -> tensor<5x2xf64>
+ %0 = toy.reshape (%arg1 : tensor<10xf64>) to tensor<5x2xf64>
```
}];
let arguments = (ins F64Tensor:$input);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
+ // Enable registering canonicalization patterns with this operation.
let hasCanonicalizer = 1;
// We expect that the reshape operation returns a statically shaped tensor.
// value must match the return type of the enclosing function.
let arguments = (ins Variadic<Toy_Type>:$input);
+ // The return operation only emits the input in the format if it is present.
+ let assemblyFormat = "($input^ `:` type($input))? attr-dict ";
+
// Allow building a ReturnOp with no return operand.
let builders = [OpBuilder<
"Builder *b, OperationState &state", [{ build(b, state, llvm::None); }]
}];
let arguments = (ins Toy_StructType:$input, I64Attr:$index);
- let results = (outs Toy_Type);
+ let results = (outs Toy_Type:$output);
+
+ let assemblyFormat = [{
+ $input `[` $index `]` attr-dict `:` type($input) `->` type($output)
+ }];
// Allow building a StructAccessOp with just a struct value and an index.
let builders = [
as an array of other constant values. For example:
```mlir
- %0 = "toy.struct_constant"() {
- value = [dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64>]
- } : () -> !toy.struct<tensor<*xf64>>
+ %0 = toy.struct_constant [
+ dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64>
+ ] : !toy.struct<tensor<*xf64>>
```
}];
- let hasFolder = 1;
let arguments = (ins ArrayAttr:$value);
- let results = (outs Toy_StructType);
+ let results = (outs Toy_StructType:$output);
+
+ let assemblyFormat = "$value attr-dict `:` type($output)";
+
let verifier = [{ return ::verify(*this); }];
+ let hasFolder = 1;
}
def TransposeOp : Toy_Op<"transpose",
let arguments = (ins F64Tensor:$input);
let results = (outs F64Tensor);
+
+ let assemblyFormat = [{
+ `(` $input `:` type($input) `)` attr-dict `to` type(results)
+ }];
+
+ // Enable registering canonicalization patterns with this operation.
let hasCanonicalizer = 1;
// Allow building a TransposeOp with from the input operand.
#include "mlir/IR/Builders.h"
#include "mlir/IR/DialectImplementation.h"
+#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/Transforms/InliningUtils.h"
// Toy Operations
//===----------------------------------------------------------------------===//
+/// A generalized parser for binary operations. This parses the different forms
+/// of 'printBinaryOp' below.
+static mlir::ParseResult parseBinaryOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ SmallVector<mlir::OpAsmParser::OperandType, 2> operands;
+ llvm::SMLoc operandsLoc = parser.getCurrentLocation();
+ Type type;
+ if (parser.parseOperandList(operands, /*requiredOperandCount=*/2) ||
+ parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseColonType(type))
+ return mlir::failure();
+
+ // If the type is a function type, it contains the input and result types of
+ // this operation.
+ if (FunctionType funcType = type.dyn_cast<FunctionType>()) {
+ if (parser.resolveOperands(operands, funcType.getInputs(), operandsLoc,
+ result.operands))
+ return mlir::failure();
+ result.addTypes(funcType.getResults());
+ return mlir::success();
+ }
+
+ // Otherwise, the parsed type is the type of both operands and results.
+ if (parser.resolveOperands(operands, type, result.operands))
+ return mlir::failure();
+ result.addTypes(type);
+ return mlir::success();
+}
+
+/// A generalized printer for binary operations. It prints in two different
+/// forms depending on if all of the types match.
+static void printBinaryOp(mlir::OpAsmPrinter &printer, mlir::Operation *op) {
+ printer << op->getName() << " " << op->getOperands();
+ printer.printOptionalAttrDict(op->getAttrs());
+ printer << " : ";
+
+ // If all of the types are the same, print the type directly.
+ Type resultType = *op->result_type_begin();
+ if (llvm::all_of(op->getOperandTypes(),
+ [=](Type type) { return type == resultType; })) {
+ printer << resultType;
+ return;
+ }
+
+ // Otherwise, print a functional type.
+ printer.printFunctionalType(op->getOperandTypes(), op->getResultTypes());
+}
+
//===----------------------------------------------------------------------===//
// ConstantOp
ConstantOp::build(builder, state, dataType, dataAttribute);
}
+/// The 'OpAsmPrinter' class provides a collection of methods for parsing
+/// various punctuation, as well as attributes, operands, types, etc. Each of
+/// these methods returns a `ParseResult`. This class is a wrapper around
+/// `LogicalResult` that can be converted to a boolean `true` value on failure,
+/// or `false` on success. This allows for easily chaining together a set of
+/// parser rules. These rules are used to populate an `mlir::OperationState`
+/// similarly to the `build` methods described above.
+static mlir::ParseResult parseConstantOp(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ mlir::DenseElementsAttr value;
+ if (parser.parseOptionalAttrDict(result.attributes) ||
+ parser.parseAttribute(value, "value", result.attributes))
+ return failure();
+
+ result.addTypes(value.getType());
+ return success();
+}
+
+/// The 'OpAsmPrinter' class is a stream that will allows for formatting
+/// strings, attributes, operands, types, etc.
+static void print(mlir::OpAsmPrinter &printer, ConstantOp op) {
+ printer << "toy.constant ";
+ printer.printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"value"});
+ printer << op.value();
+}
+
/// Verify that the given attribute value is valid for the given type.
static mlir::LogicalResult verifyConstantForType(mlir::Type type,
mlir::Attribute opaqueValue,
# CHECK-LABEL: func @multiply_transpose(
# CHECK-SAME: [[VAL_0:%.*]]: tensor<*xf64>, [[VAL_1:%.*]]: tensor<*xf64>) -> tensor<*xf64>
-# CHECK: [[VAL_2:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_3:%.*]] = "toy.transpose"([[VAL_1]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_4:%.*]] = "toy.mul"([[VAL_2]], [[VAL_3]]) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.return"([[VAL_4]]) : (tensor<*xf64>) -> ()
+# CHECK: [[VAL_2:%.*]] = toy.transpose([[VAL_0]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_3:%.*]] = toy.transpose([[VAL_1]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_4:%.*]] = toy.mul [[VAL_2]], [[VAL_3]] : tensor<*xf64>
+# CHECK-NEXT: toy.return [[VAL_4]] : tensor<*xf64>
# CHECK-LABEL: func @main()
-# CHECK-NEXT: [[VAL_5:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_6:%.*]] = "toy.reshape"([[VAL_5]]) : (tensor<2x3xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_7:%.*]] = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
-# CHECK-NEXT: [[VAL_8:%.*]] = "toy.reshape"([[VAL_7]]) : (tensor<6xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_9:%.*]] = "toy.generic_call"([[VAL_6]], [[VAL_8]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_10:%.*]] = "toy.generic_call"([[VAL_8]], [[VAL_6]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.print"([[VAL_10]]) : (tensor<*xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: [[VAL_5:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_6:%.*]] = toy.reshape([[VAL_5]] : tensor<2x3xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_7:%.*]] = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+# CHECK-NEXT: [[VAL_8:%.*]] = toy.reshape([[VAL_7]] : tensor<6xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_9:%.*]] = toy.generic_call @multiply_transpose([[VAL_6]], [[VAL_8]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: [[VAL_10:%.*]] = toy.generic_call @multiply_transpose([[VAL_8]], [[VAL_6]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: toy.print [[VAL_10]] : tensor<*xf64>
+# CHECK-NEXT: toy.return
}
# CHECK-LABEL: func @main() {
-# CHECK-NEXT: %0 = "toy.constant"() {value = dense<5.500000e+00> : tensor<f64>} : () -> tensor<f64>
-# CHECK-NEXT: %1 = "toy.reshape"(%0) : (tensor<f64>) -> tensor<2x2xf64>
-# CHECK-NEXT: "toy.print"(%1) : (tensor<2x2xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: %0 = toy.constant dense<5.500000e+00> : tensor<f64>
+# CHECK-NEXT: %1 = toy.reshape(%0 : tensor<f64>) to tensor<2x2xf64>
+# CHECK-NEXT: toy.print %1 : tensor<2x2xf64>
+# CHECK-NEXT: toy.return
# CHECK-NEXT: }
# CHECK-LABEL: func @multiply_transpose(
# CHECK-SAME: [[VAL_0:%.*]]: tensor<*xf64>, [[VAL_1:%.*]]: tensor<*xf64>) -> tensor<*xf64>
-# CHECK: [[VAL_2:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_3:%.*]] = "toy.transpose"([[VAL_1]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_4:%.*]] = "toy.mul"([[VAL_2]], [[VAL_3]]) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.return"([[VAL_4]]) : (tensor<*xf64>) -> ()
+# CHECK: [[VAL_2:%.*]] = toy.transpose([[VAL_0]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_3:%.*]] = toy.transpose([[VAL_1]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_4:%.*]] = toy.mul [[VAL_2]], [[VAL_3]] : tensor<*xf64>
+# CHECK-NEXT: toy.return [[VAL_4]] : tensor<*xf64>
# CHECK-LABEL: func @main()
-# CHECK-NEXT: [[VAL_5:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_6:%.*]] = "toy.reshape"([[VAL_5]]) : (tensor<2x3xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_7:%.*]] = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
-# CHECK-NEXT: [[VAL_8:%.*]] = "toy.reshape"([[VAL_7]]) : (tensor<6xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_9:%.*]] = "toy.generic_call"([[VAL_6]], [[VAL_8]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_10:%.*]] = "toy.generic_call"([[VAL_8]], [[VAL_6]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.print"([[VAL_10]]) : (tensor<*xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: [[VAL_5:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_6:%.*]] = toy.reshape([[VAL_5]] : tensor<2x3xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_7:%.*]] = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+# CHECK-NEXT: [[VAL_8:%.*]] = toy.reshape([[VAL_7]] : tensor<6xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_9:%.*]] = toy.generic_call @multiply_transpose([[VAL_6]], [[VAL_8]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: [[VAL_10:%.*]] = toy.generic_call @multiply_transpose([[VAL_8]], [[VAL_6]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: toy.print [[VAL_10]] : tensor<*xf64>
+# CHECK-NEXT: toy.return
}
# CHECK-LABEL: func @main() {
-# CHECK-NEXT: %0 = "toy.constant"() {value = dense<5.500000e+00> : tensor<f64>} : () -> tensor<f64>
-# CHECK-NEXT: %1 = "toy.reshape"(%0) : (tensor<f64>) -> tensor<2x2xf64>
-# CHECK-NEXT: "toy.print"(%1) : (tensor<2x2xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: %0 = toy.constant dense<5.500000e+00> : tensor<f64>
+# CHECK-NEXT: %1 = toy.reshape(%0 : tensor<f64>) to tensor<2x2xf64>
+# CHECK-NEXT: toy.print %1 : tensor<2x2xf64>
+# CHECK-NEXT: toy.return
# CHECK-NEXT: }
# CHECK-LABEL: func @multiply_transpose(
# CHECK-SAME: [[VAL_0:%.*]]: tensor<*xf64>, [[VAL_1:%.*]]: tensor<*xf64>) -> tensor<*xf64>
-# CHECK: [[VAL_2:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_3:%.*]] = "toy.transpose"([[VAL_1]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_4:%.*]] = "toy.mul"([[VAL_2]], [[VAL_3]]) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.return"([[VAL_4]]) : (tensor<*xf64>) -> ()
+# CHECK: [[VAL_2:%.*]] = toy.transpose([[VAL_0]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_3:%.*]] = toy.transpose([[VAL_1]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_4:%.*]] = toy.mul [[VAL_2]], [[VAL_3]] : tensor<*xf64>
+# CHECK-NEXT: toy.return [[VAL_4]] : tensor<*xf64>
# CHECK-LABEL: func @main()
-# CHECK-NEXT: [[VAL_5:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_6:%.*]] = "toy.reshape"([[VAL_5]]) : (tensor<2x3xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_7:%.*]] = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
-# CHECK-NEXT: [[VAL_8:%.*]] = "toy.reshape"([[VAL_7]]) : (tensor<6xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_9:%.*]] = "toy.generic_call"([[VAL_6]], [[VAL_8]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_10:%.*]] = "toy.generic_call"([[VAL_8]], [[VAL_6]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.print"([[VAL_10]]) : (tensor<*xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: [[VAL_5:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_6:%.*]] = toy.reshape([[VAL_5]] : tensor<2x3xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_7:%.*]] = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+# CHECK-NEXT: [[VAL_8:%.*]] = toy.reshape([[VAL_7]] : tensor<6xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_9:%.*]] = toy.generic_call @multiply_transpose([[VAL_6]], [[VAL_8]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: [[VAL_10:%.*]] = toy.generic_call @multiply_transpose([[VAL_8]], [[VAL_6]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: toy.print [[VAL_10]] : tensor<*xf64>
+# CHECK-NEXT: toy.return
}
# CHECK-LABEL: func @main() {
-# CHECK-NEXT: %0 = "toy.constant"() {value = dense<5.500000e+00> : tensor<f64>} : () -> tensor<f64>
-# CHECK-NEXT: %1 = "toy.reshape"(%0) : (tensor<f64>) -> tensor<2x2xf64>
-# CHECK-NEXT: "toy.print"(%1) : (tensor<2x2xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: %0 = toy.constant dense<5.500000e+00> : tensor<f64>
+# CHECK-NEXT: %1 = toy.reshape(%0 : tensor<f64>) to tensor<2x2xf64>
+# CHECK-NEXT: toy.print %1 : tensor<2x2xf64>
+# CHECK-NEXT: toy.return
# CHECK-NEXT: }
func @multiply_transpose(%arg0: tensor<*xf64>, %arg1: tensor<*xf64>) -> tensor<*xf64>
attributes { sym_visibility = "private" } {
- %0 = "toy.transpose"(%arg0) : (tensor<*xf64>) -> tensor<*xf64>
- %1 = "toy.transpose"(%arg1) : (tensor<*xf64>) -> tensor<*xf64>
- %2 = "toy.mul"(%0, %1) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
- "toy.return"(%2) : (tensor<*xf64>) -> ()
+ %0 = toy.transpose(%arg0 : tensor<*xf64>) to tensor<*xf64>
+ %1 = toy.transpose(%arg1 : tensor<*xf64>) to tensor<*xf64>
+ %2 = toy.mul %0, %1 : tensor<*xf64>
+ toy.return %2 : tensor<*xf64>
}
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %1 = "toy.reshape"(%0) : (tensor<2x3xf64>) -> tensor<2x3xf64>
- %2 = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
- %3 = "toy.reshape"(%2) : (tensor<6xf64>) -> tensor<2x3xf64>
- %4 = "toy.generic_call"(%1, %3) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
- %5 = "toy.generic_call"(%3, %1) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
- "toy.print"(%5) : (tensor<*xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %1 = toy.reshape(%0 : tensor<2x3xf64>) to tensor<2x3xf64>
+ %2 = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+ %3 = toy.reshape(%2 : tensor<6xf64>) to tensor<2x3xf64>
+ %4 = toy.generic_call @multiply_transpose(%1, %3) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+ %5 = toy.generic_call @multiply_transpose(%3, %1) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+ toy.print %5 : tensor<*xf64>
+ toy.return
}
// CHECK-NOT: func @multiply_transpose
// CHECK-NOT: tensor<*xf64>
// CHECK-LABEL: func @main()
-// CHECK: [[VAL_0:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-// CHECK: [[VAL_1:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<2x3xf64>) -> tensor<3x2xf64>
-// CHECK: [[VAL_2:%.*]] = "toy.mul"([[VAL_1]], [[VAL_1]]) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
-// CHECK: "toy.print"([[VAL_2]]) : (tensor<3x2xf64>) -> ()
-// CHECK: "toy.return"() : () -> ()
+// CHECK: [[VAL_0:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+// CHECK: [[VAL_1:%.*]] = toy.transpose([[VAL_0]] : tensor<2x3xf64>) to tensor<3x2xf64>
+// CHECK: [[VAL_2:%.*]] = toy.mul [[VAL_1]], [[VAL_1]] : tensor<3x2xf64>
+// CHECK: toy.print [[VAL_2]] : tensor<3x2xf64>
+// CHECK: toy.return
// RUN: toyc-ch5 %s -emit=mlir-affine -opt 2>&1 | FileCheck %s --check-prefix=OPT
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %2 = "toy.transpose"(%0) : (tensor<2x3xf64>) -> tensor<3x2xf64>
- %3 = "toy.mul"(%2, %2) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
- "toy.print"(%3) : (tensor<3x2xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %2 = toy.transpose(%0 : tensor<2x3xf64>) to tensor<3x2xf64>
+ %3 = toy.mul %2, %2 : tensor<3x2xf64>
+ toy.print %3 : tensor<3x2xf64>
+ toy.return
}
// CHECK-LABEL: func @main()
// CHECK: [[VAL_15:%.*]] = affine.load [[VAL_7]]{{\[}}[[VAL_12]], [[VAL_13]]] : memref<3x2xf64>
// CHECK: [[VAL_16:%.*]] = mulf [[VAL_14]], [[VAL_15]] : f64
// CHECK: affine.store [[VAL_16]], [[VAL_6]]{{\[}}[[VAL_12]], [[VAL_13]]] : memref<3x2xf64>
-// CHECK: "toy.print"([[VAL_6]]) : (memref<3x2xf64>) -> ()
+// CHECK: toy.print [[VAL_6]] : memref<3x2xf64>
// CHECK: dealloc [[VAL_8]] : memref<2x3xf64>
// CHECK: dealloc [[VAL_7]] : memref<3x2xf64>
// CHECK: dealloc [[VAL_6]] : memref<3x2xf64>
// OPT: [[VAL_10:%.*]] = affine.load [[VAL_7]]{{\[}}[[VAL_9]], [[VAL_8]]] : memref<2x3xf64>
// OPT: [[VAL_11:%.*]] = mulf [[VAL_10]], [[VAL_10]] : f64
// OPT: affine.store [[VAL_11]], [[VAL_6]]{{\[}}[[VAL_8]], [[VAL_9]]] : memref<3x2xf64>
-// OPT: "toy.print"([[VAL_6]]) : (memref<3x2xf64>) -> ()
+// OPT: toy.print [[VAL_6]] : memref<3x2xf64>
// OPT: dealloc [[VAL_7]] : memref<2x3xf64>
// OPT: dealloc [[VAL_6]] : memref<3x2xf64>
# CHECK-LABEL: func @multiply_transpose(
# CHECK-SAME: [[VAL_0:%.*]]: tensor<*xf64>, [[VAL_1:%.*]]: tensor<*xf64>) -> tensor<*xf64>
-# CHECK: [[VAL_2:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_3:%.*]] = "toy.transpose"([[VAL_1]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_4:%.*]] = "toy.mul"([[VAL_2]], [[VAL_3]]) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.return"([[VAL_4]]) : (tensor<*xf64>) -> ()
+# CHECK: [[VAL_2:%.*]] = toy.transpose([[VAL_0]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_3:%.*]] = toy.transpose([[VAL_1]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_4:%.*]] = toy.mul [[VAL_2]], [[VAL_3]] : tensor<*xf64>
+# CHECK-NEXT: toy.return [[VAL_4]] : tensor<*xf64>
# CHECK-LABEL: func @main()
-# CHECK-NEXT: [[VAL_5:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_6:%.*]] = "toy.reshape"([[VAL_5]]) : (tensor<2x3xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_7:%.*]] = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
-# CHECK-NEXT: [[VAL_8:%.*]] = "toy.reshape"([[VAL_7]]) : (tensor<6xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_9:%.*]] = "toy.generic_call"([[VAL_6]], [[VAL_8]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_10:%.*]] = "toy.generic_call"([[VAL_8]], [[VAL_6]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.print"([[VAL_10]]) : (tensor<*xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: [[VAL_5:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_6:%.*]] = toy.reshape([[VAL_5]] : tensor<2x3xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_7:%.*]] = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+# CHECK-NEXT: [[VAL_8:%.*]] = toy.reshape([[VAL_7]] : tensor<6xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_9:%.*]] = toy.generic_call @multiply_transpose([[VAL_6]], [[VAL_8]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: [[VAL_10:%.*]] = toy.generic_call @multiply_transpose([[VAL_8]], [[VAL_6]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: toy.print [[VAL_10]] : tensor<*xf64>
+# CHECK-NEXT: toy.return
}
# CHECK-LABEL: func @main() {
-# CHECK-NEXT: %0 = "toy.constant"() {value = dense<5.500000e+00> : tensor<f64>} : () -> tensor<f64>
-# CHECK-NEXT: %1 = "toy.reshape"(%0) : (tensor<f64>) -> tensor<2x2xf64>
-# CHECK-NEXT: "toy.print"(%1) : (tensor<2x2xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: %0 = toy.constant dense<5.500000e+00> : tensor<f64>
+# CHECK-NEXT: %1 = toy.reshape(%0 : tensor<f64>) to tensor<2x2xf64>
+# CHECK-NEXT: toy.print %1 : tensor<2x2xf64>
+# CHECK-NEXT: toy.return
# CHECK-NEXT: }
func @multiply_transpose(%arg0: tensor<*xf64>, %arg1: tensor<*xf64>) -> tensor<*xf64>
attributes { sym_visibility = "private" } {
- %0 = "toy.transpose"(%arg0) : (tensor<*xf64>) -> tensor<*xf64>
- %1 = "toy.transpose"(%arg1) : (tensor<*xf64>) -> tensor<*xf64>
- %2 = "toy.mul"(%0, %1) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
- "toy.return"(%2) : (tensor<*xf64>) -> ()
+ %0 = toy.transpose(%arg0 : tensor<*xf64>) to tensor<*xf64>
+ %1 = toy.transpose(%arg1 : tensor<*xf64>) to tensor<*xf64>
+ %2 = toy.mul %0, %1 : tensor<*xf64>
+ toy.return %2 : tensor<*xf64>
}
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %1 = "toy.reshape"(%0) : (tensor<2x3xf64>) -> tensor<2x3xf64>
- %2 = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
- %3 = "toy.reshape"(%2) : (tensor<6xf64>) -> tensor<2x3xf64>
- %4 = "toy.generic_call"(%1, %3) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
- %5 = "toy.generic_call"(%3, %1) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
- "toy.print"(%5) : (tensor<*xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %1 = toy.reshape(%0 : tensor<2x3xf64>) to tensor<2x3xf64>
+ %2 = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+ %3 = toy.reshape(%2 : tensor<6xf64>) to tensor<2x3xf64>
+ %4 = toy.generic_call @multiply_transpose(%1, %3) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+ %5 = toy.generic_call @multiply_transpose(%3, %1) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+ toy.print %5 : tensor<*xf64>
+ toy.return
}
// CHECK-NOT: func @multiply_transpose
// CHECK-NOT: tensor<*xf64>
// CHECK-LABEL: func @main()
-// CHECK: [[VAL_0:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-// CHECK: [[VAL_1:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<2x3xf64>) -> tensor<3x2xf64>
-// CHECK: [[VAL_2:%.*]] = "toy.mul"([[VAL_1]], [[VAL_1]]) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
-// CHECK: "toy.print"([[VAL_2]]) : (tensor<3x2xf64>) -> ()
-// CHECK: "toy.return"() : () -> ()
+// CHECK: [[VAL_0:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+// CHECK: [[VAL_1:%.*]] = toy.transpose([[VAL_0]] : tensor<2x3xf64>) to tensor<3x2xf64>
+// CHECK: [[VAL_2:%.*]] = toy.mul [[VAL_1]], [[VAL_1]] : tensor<3x2xf64>
+// CHECK: toy.print [[VAL_2]] : tensor<3x2xf64>
+// CHECK: toy.return
// RUN: toyc-ch6 %s -emit=mlir-affine -opt 2>&1 | FileCheck %s --check-prefix=OPT
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %2 = "toy.transpose"(%0) : (tensor<2x3xf64>) -> tensor<3x2xf64>
- %3 = "toy.mul"(%2, %2) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
- "toy.print"(%3) : (tensor<3x2xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %2 = toy.transpose(%0 : tensor<2x3xf64>) to tensor<3x2xf64>
+ %3 = toy.mul %2, %2 : tensor<3x2xf64>
+ toy.print %3 : tensor<3x2xf64>
+ toy.return
}
// CHECK-LABEL: func @main()
// CHECK: [[VAL_15:%.*]] = affine.load [[VAL_7]]{{\[}}[[VAL_12]], [[VAL_13]]] : memref<3x2xf64>
// CHECK: [[VAL_16:%.*]] = mulf [[VAL_14]], [[VAL_15]] : f64
// CHECK: affine.store [[VAL_16]], [[VAL_6]]{{\[}}[[VAL_12]], [[VAL_13]]] : memref<3x2xf64>
-// CHECK: "toy.print"([[VAL_6]]) : (memref<3x2xf64>) -> ()
+// CHECK: toy.print [[VAL_6]] : memref<3x2xf64>
// CHECK: dealloc [[VAL_8]] : memref<2x3xf64>
// CHECK: dealloc [[VAL_7]] : memref<3x2xf64>
// CHECK: dealloc [[VAL_6]] : memref<3x2xf64>
// OPT: [[VAL_10:%.*]] = affine.load [[VAL_7]]{{\[}}[[VAL_9]], [[VAL_8]]] : memref<2x3xf64>
// OPT: [[VAL_11:%.*]] = mulf [[VAL_10]], [[VAL_10]] : f64
// OPT: affine.store [[VAL_11]], [[VAL_6]]{{\[}}[[VAL_8]], [[VAL_9]]] : memref<3x2xf64>
-// OPT: "toy.print"([[VAL_6]]) : (memref<3x2xf64>) -> ()
+// OPT: toy.print [[VAL_6]] : memref<3x2xf64>
// OPT: dealloc [[VAL_7]] : memref<2x3xf64>
// OPT: dealloc [[VAL_6]] : memref<3x2xf64>
# CHECK-LABEL: func @multiply_transpose(
# CHECK-SAME: [[VAL_0:%.*]]: tensor<*xf64>, [[VAL_1:%.*]]: tensor<*xf64>) -> tensor<*xf64>
-# CHECK: [[VAL_2:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_3:%.*]] = "toy.transpose"([[VAL_1]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_4:%.*]] = "toy.mul"([[VAL_2]], [[VAL_3]]) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.return"([[VAL_4]]) : (tensor<*xf64>) -> ()
+# CHECK: [[VAL_2:%.*]] = toy.transpose([[VAL_0]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_3:%.*]] = toy.transpose([[VAL_1]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_4:%.*]] = toy.mul [[VAL_2]], [[VAL_3]] : tensor<*xf64>
+# CHECK-NEXT: toy.return [[VAL_4]] : tensor<*xf64>
# CHECK-LABEL: func @main()
-# CHECK-NEXT: [[VAL_5:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_6:%.*]] = "toy.reshape"([[VAL_5]]) : (tensor<2x3xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_7:%.*]] = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
-# CHECK-NEXT: [[VAL_8:%.*]] = "toy.reshape"([[VAL_7]]) : (tensor<6xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_9:%.*]] = "toy.generic_call"([[VAL_6]], [[VAL_8]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_10:%.*]] = "toy.generic_call"([[VAL_8]], [[VAL_6]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.print"([[VAL_10]]) : (tensor<*xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: [[VAL_5:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_6:%.*]] = toy.reshape([[VAL_5]] : tensor<2x3xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_7:%.*]] = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+# CHECK-NEXT: [[VAL_8:%.*]] = toy.reshape([[VAL_7]] : tensor<6xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_9:%.*]] = toy.generic_call @multiply_transpose([[VAL_6]], [[VAL_8]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: [[VAL_10:%.*]] = toy.generic_call @multiply_transpose([[VAL_8]], [[VAL_6]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: toy.print [[VAL_10]] : tensor<*xf64>
+# CHECK-NEXT: toy.return
// RUN: toyc-ch6 %s -emit=llvm -opt
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %2 = "toy.transpose"(%0) : (tensor<2x3xf64>) -> tensor<3x2xf64>
- %3 = "toy.mul"(%2, %2) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
- "toy.print"(%3) : (tensor<3x2xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %2 = toy.transpose(%0 : tensor<2x3xf64>) to tensor<3x2xf64>
+ %3 = toy.mul %2, %2 : tensor<3x2xf64>
+ toy.print %3 : tensor<3x2xf64>
+ toy.return
}
// CHECK-LABEL: define void @main()
}
# CHECK-LABEL: func @main() {
-# CHECK-NEXT: %0 = "toy.constant"() {value = dense<5.500000e+00> : tensor<f64>} : () -> tensor<f64>
-# CHECK-NEXT: %1 = "toy.reshape"(%0) : (tensor<f64>) -> tensor<2x2xf64>
-# CHECK-NEXT: "toy.print"(%1) : (tensor<2x2xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: %0 = toy.constant dense<5.500000e+00> : tensor<f64>
+# CHECK-NEXT: %1 = toy.reshape(%0 : tensor<f64>) to tensor<2x2xf64>
+# CHECK-NEXT: toy.print %1 : tensor<2x2xf64>
+# CHECK-NEXT: toy.return
# CHECK-NEXT: }
func @multiply_transpose(%arg0: tensor<*xf64>, %arg1: tensor<*xf64>) -> tensor<*xf64>
attributes { sym_visibility = "private" } {
- %0 = "toy.transpose"(%arg0) : (tensor<*xf64>) -> tensor<*xf64>
- %1 = "toy.transpose"(%arg1) : (tensor<*xf64>) -> tensor<*xf64>
- %2 = "toy.mul"(%0, %1) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
- "toy.return"(%2) : (tensor<*xf64>) -> ()
+ %0 = toy.transpose(%arg0 : tensor<*xf64>) to tensor<*xf64>
+ %1 = toy.transpose(%arg1 : tensor<*xf64>) to tensor<*xf64>
+ %2 = toy.mul %0, %1 : tensor<*xf64>
+ toy.return %2 : tensor<*xf64>
}
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %1 = "toy.reshape"(%0) : (tensor<2x3xf64>) -> tensor<2x3xf64>
- %2 = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
- %3 = "toy.reshape"(%2) : (tensor<6xf64>) -> tensor<2x3xf64>
- %4 = "toy.generic_call"(%1, %3) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
- %5 = "toy.generic_call"(%3, %1) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
- "toy.print"(%5) : (tensor<*xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %1 = toy.reshape(%0 : tensor<2x3xf64>) to tensor<2x3xf64>
+ %2 = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+ %3 = toy.reshape(%2 : tensor<6xf64>) to tensor<2x3xf64>
+ %4 = toy.generic_call @multiply_transpose(%1, %3) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+ %5 = toy.generic_call @multiply_transpose(%3, %1) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+ toy.print %5 : tensor<*xf64>
+ toy.return
}
// CHECK-NOT: func @multiply_transpose
// CHECK-NOT: tensor<*xf64>
// CHECK-LABEL: func @main()
-// CHECK: [[VAL_0:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-// CHECK: [[VAL_1:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<2x3xf64>) -> tensor<3x2xf64>
-// CHECK: [[VAL_2:%.*]] = "toy.mul"([[VAL_1]], [[VAL_1]]) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
-// CHECK: "toy.print"([[VAL_2]]) : (tensor<3x2xf64>) -> ()
-// CHECK: "toy.return"() : () -> ()
+// CHECK: [[VAL_0:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+// CHECK: [[VAL_1:%.*]] = toy.transpose([[VAL_0]] : tensor<2x3xf64>) to tensor<3x2xf64>
+// CHECK: [[VAL_2:%.*]] = toy.mul [[VAL_1]], [[VAL_1]] : tensor<3x2xf64>
+// CHECK: toy.print [[VAL_2]] : tensor<3x2xf64>
+// CHECK: toy.return
// RUN: toyc-ch7 %s -emit=mlir-affine -opt 2>&1 | FileCheck %s --check-prefix=OPT
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %2 = "toy.transpose"(%0) : (tensor<2x3xf64>) -> tensor<3x2xf64>
- %3 = "toy.mul"(%2, %2) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
- "toy.print"(%3) : (tensor<3x2xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %2 = toy.transpose(%0 : tensor<2x3xf64>) to tensor<3x2xf64>
+ %3 = toy.mul %2, %2 : tensor<3x2xf64>
+ toy.print %3 : tensor<3x2xf64>
+ toy.return
}
// CHECK-LABEL: func @main()
// CHECK: [[VAL_15:%.*]] = affine.load [[VAL_7]]{{\[}}[[VAL_12]], [[VAL_13]]] : memref<3x2xf64>
// CHECK: [[VAL_16:%.*]] = mulf [[VAL_14]], [[VAL_15]] : f64
// CHECK: affine.store [[VAL_16]], [[VAL_6]]{{\[}}[[VAL_12]], [[VAL_13]]] : memref<3x2xf64>
-// CHECK: "toy.print"([[VAL_6]]) : (memref<3x2xf64>) -> ()
+// CHECK: toy.print [[VAL_6]] : memref<3x2xf64>
// CHECK: dealloc [[VAL_8]] : memref<2x3xf64>
// CHECK: dealloc [[VAL_7]] : memref<3x2xf64>
// CHECK: dealloc [[VAL_6]] : memref<3x2xf64>
// OPT: [[VAL_10:%.*]] = affine.load [[VAL_7]]{{\[}}[[VAL_9]], [[VAL_8]]] : memref<2x3xf64>
// OPT: [[VAL_11:%.*]] = mulf [[VAL_10]], [[VAL_10]] : f64
// OPT: affine.store [[VAL_11]], [[VAL_6]]{{\[}}[[VAL_8]], [[VAL_9]]] : memref<3x2xf64>
-// OPT: "toy.print"([[VAL_6]]) : (memref<3x2xf64>) -> ()
+// OPT: toy.print [[VAL_6]] : memref<3x2xf64>
// OPT: dealloc [[VAL_7]] : memref<2x3xf64>
// OPT: dealloc [[VAL_6]] : memref<3x2xf64>
# CHECK-LABEL: func @multiply_transpose(
# CHECK-SAME: [[VAL_0:%.*]]: tensor<*xf64>, [[VAL_1:%.*]]: tensor<*xf64>) -> tensor<*xf64>
-# CHECK: [[VAL_2:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_3:%.*]] = "toy.transpose"([[VAL_1]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_4:%.*]] = "toy.mul"([[VAL_2]], [[VAL_3]]) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.return"([[VAL_4]]) : (tensor<*xf64>) -> ()
+# CHECK: [[VAL_2:%.*]] = toy.transpose([[VAL_0]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_3:%.*]] = toy.transpose([[VAL_1]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_4:%.*]] = toy.mul [[VAL_2]], [[VAL_3]] : tensor<*xf64>
+# CHECK-NEXT: toy.return [[VAL_4]] : tensor<*xf64>
# CHECK-LABEL: func @main()
-# CHECK-NEXT: [[VAL_5:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_6:%.*]] = "toy.reshape"([[VAL_5]]) : (tensor<2x3xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_7:%.*]] = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
-# CHECK-NEXT: [[VAL_8:%.*]] = "toy.reshape"([[VAL_7]]) : (tensor<6xf64>) -> tensor<2x3xf64>
-# CHECK-NEXT: [[VAL_9:%.*]] = "toy.generic_call"([[VAL_6]], [[VAL_8]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_10:%.*]] = "toy.generic_call"([[VAL_8]], [[VAL_6]]) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.print"([[VAL_10]]) : (tensor<*xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: [[VAL_5:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_6:%.*]] = toy.reshape([[VAL_5]] : tensor<2x3xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_7:%.*]] = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+# CHECK-NEXT: [[VAL_8:%.*]] = toy.reshape([[VAL_7]] : tensor<6xf64>) to tensor<2x3xf64>
+# CHECK-NEXT: [[VAL_9:%.*]] = toy.generic_call @multiply_transpose([[VAL_6]], [[VAL_8]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: [[VAL_10:%.*]] = toy.generic_call @multiply_transpose([[VAL_8]], [[VAL_6]]) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+# CHECK-NEXT: toy.print [[VAL_10]] : tensor<*xf64>
+# CHECK-NEXT: toy.return
// RUN: toyc-ch7 %s -emit=llvm -opt
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %2 = "toy.transpose"(%0) : (tensor<2x3xf64>) -> tensor<3x2xf64>
- %3 = "toy.mul"(%2, %2) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
- "toy.print"(%3) : (tensor<3x2xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %2 = toy.transpose(%0 : tensor<2x3xf64>) to tensor<3x2xf64>
+ %3 = toy.mul %2, %2 : tensor<3x2xf64>
+ toy.print %3 : tensor<3x2xf64>
+ toy.return
}
// CHECK-LABEL: define void @main()
}
# CHECK-LABEL: func @main() {
-# CHECK-NEXT: %0 = "toy.constant"() {value = dense<5.500000e+00> : tensor<f64>} : () -> tensor<f64>
-# CHECK-NEXT: %1 = "toy.reshape"(%0) : (tensor<f64>) -> tensor<2x2xf64>
-# CHECK-NEXT: "toy.print"(%1) : (tensor<2x2xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: %0 = toy.constant dense<5.500000e+00> : tensor<f64>
+# CHECK-NEXT: %1 = toy.reshape(%0 : tensor<f64>) to tensor<2x2xf64>
+# CHECK-NEXT: toy.print %1 : tensor<2x2xf64>
+# CHECK-NEXT: toy.return
# CHECK-NEXT: }
func @multiply_transpose(%arg0: tensor<*xf64>, %arg1: tensor<*xf64>) -> tensor<*xf64>
attributes { sym_visibility = "private" } {
- %0 = "toy.transpose"(%arg0) : (tensor<*xf64>) -> tensor<*xf64>
- %1 = "toy.transpose"(%arg1) : (tensor<*xf64>) -> tensor<*xf64>
- %2 = "toy.mul"(%0, %1) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
- "toy.return"(%2) : (tensor<*xf64>) -> ()
+ %0 = toy.transpose(%arg0 : tensor<*xf64>) to tensor<*xf64>
+ %1 = toy.transpose(%arg1 : tensor<*xf64>) to tensor<*xf64>
+ %2 = toy.mul %0, %1 : tensor<*xf64>
+ toy.return %2 : tensor<*xf64>
}
func @main() {
- %0 = "toy.constant"() {value = dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
- %1 = "toy.reshape"(%0) : (tensor<2x3xf64>) -> tensor<2x3xf64>
- %2 = "toy.constant"() {value = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>} : () -> tensor<6xf64>
- %3 = "toy.reshape"(%2) : (tensor<6xf64>) -> tensor<2x3xf64>
- %4 = "toy.generic_call"(%1, %3) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
- %5 = "toy.generic_call"(%3, %1) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
- "toy.print"(%5) : (tensor<*xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.constant dense<[[1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+ %1 = toy.reshape(%0 : tensor<2x3xf64>) to tensor<2x3xf64>
+ %2 = toy.constant dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00, 6.000000e+00]> : tensor<6xf64>
+ %3 = toy.reshape(%2 : tensor<6xf64>) to tensor<2x3xf64>
+ %4 = toy.generic_call @multiply_transpose(%1, %3) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+ %5 = toy.generic_call @multiply_transpose(%3, %1) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64>
+ toy.print %5 : tensor<*xf64>
+ toy.return
}
// CHECK-NOT: func @multiply_transpose
// CHECK-NOT: tensor<*xf64>
// CHECK-LABEL: func @main()
-// CHECK: [[VAL_0:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-// CHECK: [[VAL_1:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<2x3xf64>) -> tensor<3x2xf64>
-// CHECK: [[VAL_2:%.*]] = "toy.mul"([[VAL_1]], [[VAL_1]]) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
-// CHECK: "toy.print"([[VAL_2]]) : (tensor<3x2xf64>) -> ()
-// CHECK: "toy.return"() : () -> ()
+// CHECK: [[VAL_0:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+// CHECK: [[VAL_1:%.*]] = toy.transpose([[VAL_0]] : tensor<2x3xf64>) to tensor<3x2xf64>
+// CHECK: [[VAL_2:%.*]] = toy.mul [[VAL_1]], [[VAL_1]] : tensor<3x2xf64>
+// CHECK: toy.print [[VAL_2]] : tensor<3x2xf64>
+// CHECK: toy.return
# CHECK-LABEL: func @multiply_transpose(
# CHECK-SAME: [[VAL_0:%.*]]: !toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
# CHECK-SAME: attributes {sym_visibility = "private"}
-# CHECK-NEXT: [[VAL_1:%.*]] = "toy.struct_access"([[VAL_0]]) {index = 0 : i64} : (!toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_2:%.*]] = "toy.transpose"([[VAL_1]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_3:%.*]] = "toy.struct_access"([[VAL_0]]) {index = 1 : i64} : (!toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_4:%.*]] = "toy.transpose"([[VAL_3]]) : (tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: [[VAL_5:%.*]] = "toy.mul"([[VAL_2]], [[VAL_4]]) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.return"([[VAL_5]]) : (tensor<*xf64>) -> ()
+# CHECK-NEXT: [[VAL_1:%.*]] = toy.struct_access [[VAL_0]][0] : !toy.struct<tensor<*xf64>, tensor<*xf64>> -> tensor<*xf64>
+# CHECK-NEXT: [[VAL_2:%.*]] = toy.transpose([[VAL_1]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_3:%.*]] = toy.struct_access [[VAL_0]][1] : !toy.struct<tensor<*xf64>, tensor<*xf64>> -> tensor<*xf64>
+# CHECK-NEXT: [[VAL_4:%.*]] = toy.transpose([[VAL_3]] : tensor<*xf64>) to tensor<*xf64>
+# CHECK-NEXT: [[VAL_5:%.*]] = toy.mul [[VAL_2]], [[VAL_4]] : tensor<*xf64>
+# CHECK-NEXT: toy.return [[VAL_5]] : tensor<*xf64>
# CHECK-LABEL: func @main()
-# CHECK-NEXT: [[VAL_6:%.*]] = "toy.struct_constant"() {value = [dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>, dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>]} : () -> !toy.struct<tensor<*xf64>, tensor<*xf64>>
-# CHECK-NEXT: [[VAL_7:%.*]] = "toy.generic_call"([[VAL_6]]) {callee = @multiply_transpose} : (!toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
-# CHECK-NEXT: "toy.print"([[VAL_7]]) : (tensor<*xf64>) -> ()
-# CHECK-NEXT: "toy.return"() : () -> ()
+# CHECK-NEXT: [[VAL_6:%.*]] = toy.struct_constant [dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>, dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>] : !toy.struct<tensor<*xf64>, tensor<*xf64>>
+# CHECK-NEXT: [[VAL_7:%.*]] = toy.generic_call @multiply_transpose([[VAL_6]]) : (!toy.struct<tensor<*xf64>, tensor<*xf64>>) -> tensor<*xf64>
+# CHECK-NEXT: toy.print [[VAL_7]] : tensor<*xf64>
+# CHECK-NEXT: toy.return
# OPT-LABEL: func @main()
-# OPT-NEXT: [[VAL_0:%.*]] = "toy.constant"() {value = dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
-# OPT-NEXT: [[VAL_1:%.*]] = "toy.transpose"([[VAL_0]]) : (tensor<2x3xf64>) -> tensor<3x2xf64>
-# OPT-NEXT: [[VAL_2:%.*]] = "toy.mul"([[VAL_1]], [[VAL_1]]) : (tensor<3x2xf64>, tensor<3x2xf64>) -> tensor<3x2xf64>
-# OPT-NEXT: "toy.print"([[VAL_2]]) : (tensor<3x2xf64>) -> ()
-# OPT-NEXT: "toy.return"() : () -> ()
+# OPT-NEXT: [[VAL_0:%.*]] = toy.constant dense<{{\[\[}}1.000000e+00, 2.000000e+00, 3.000000e+00], [4.000000e+00, 5.000000e+00, 6.000000e+00]]> : tensor<2x3xf64>
+# OPT-NEXT: [[VAL_1:%.*]] = toy.transpose([[VAL_0]] : tensor<2x3xf64>) to tensor<3x2xf64>
+# OPT-NEXT: [[VAL_2:%.*]] = toy.mul [[VAL_1]], [[VAL_1]] : tensor<3x2xf64>
+# OPT-NEXT: toy.print [[VAL_2]] : tensor<3x2xf64>
+# OPT-NEXT: toy.return
// RUN: toyc-ch7 %s -emit=mlir -opt 2>&1 | FileCheck %s
func @main() {
- %0 = "toy.struct_constant"() {
- value = [[dense<4.000000e+00> : tensor<2x2xf64>], dense<4.000000e+00> : tensor<2x2xf64>]
- } : () -> !toy.struct<!toy.struct<tensor<*xf64>>, tensor<*xf64>>
- %1 = "toy.struct_access"(%0) {index = 0 : i64} : (!toy.struct<!toy.struct<tensor<*xf64>>, tensor<*xf64>>) -> !toy.struct<tensor<*xf64>>
- %2 = "toy.struct_access"(%1) {index = 0 : i64} : (!toy.struct<tensor<*xf64>>) -> tensor<*xf64>
- "toy.print"(%2) : (tensor<*xf64>) -> ()
- "toy.return"() : () -> ()
+ %0 = toy.struct_constant [
+ [dense<4.000000e+00> : tensor<2x2xf64>], dense<4.000000e+00> : tensor<2x2xf64>
+ ] : !toy.struct<!toy.struct<tensor<*xf64>>, tensor<*xf64>>
+ %1 = toy.struct_access %0[0] : !toy.struct<!toy.struct<tensor<*xf64>>, tensor<*xf64>> -> !toy.struct<tensor<*xf64>>
+ %2 = toy.struct_access %1[0] : !toy.struct<tensor<*xf64>> -> tensor<*xf64>
+ toy.print %2 : tensor<*xf64>
+ toy.return
}
// CHECK-LABEL: func @main
-// CHECK-NEXT: %[[CST:.*]] = "toy.constant"
-// CHECK-SAME: dense<4.0
-// CHECK-NEXT: "toy.print"(%[[CST]])
+// CHECK-NEXT: %[[CST:.*]] = toy.constant dense<4.0
+// CHECK-NEXT: toy.print %[[CST]]