diff --git a/mlir/docs/Tutorials/Toy/Ch-2.md b/mlir/docs/Tutorials/Toy/Ch-2.md --- a/mlir/docs/Tutorials/Toy/Ch-2.md +++ b/mlir/docs/Tutorials/Toy/Ch-2.md @@ -168,19 +168,49 @@ /// constructor). It can also override virtual methods to change some general /// behavior, which will be demonstrated in later chapters of the tutorial. class ToyDialect : public mlir::Dialect { - public: +public: explicit ToyDialect(mlir::MLIRContext *ctx); /// Provide a utility accessor to the dialect namespace. This is used by /// several utilities. static llvm::StringRef getDialectNamespace() { return "toy"; } + + /// An initializer called from the constructor of ToyDialect that is used to + /// register operations, types, and more within the Toy dialect. + void initialize(); }; ``` -The dialect can now be registered in the global registry: +This is the C++ definition of a dialect, but MLIR also supports defining +dialects declaratively via tablegen. Using the declarative specification is much +cleaner as it removes the need for a large portion of the boilerplate when +defining a new dialect. In the declarative format, the toy dialect would be +specified as: + +```tablegen +// Provide a definition of the 'toy' dialect in the ODS framework so that we +// can define our operations. +def Toy_Dialect : Dialect { + // The namespace of our dialect, this corresponds 1-1 with the string we + // provided in `ToyDialect::getDialectNamespace`. + let name = "toy"; + + // The C++ namespace that the dialect class definition resides in. + let cppNamespace = "toy"; +} +``` + +To see what this generates, we can run the `mlir-tblgen` command with the +`gen-dialect-decls` action like so: + +```shell +${build_root}/bin/mlir-tblgen -gen-dialect-decls ${mlir_src_root}/examples/toy/Ch2/include/toy/Ops.td -I ${mlir_src_root}/include/ +``` + +The dialect can now be loaded into an MLIRContext: ```c++ - mlir::registerDialect(); + context.loadDialect(); ``` Any new `MLIRContext` created from now on will contain an instance of the Toy @@ -249,11 +279,10 @@ }; ``` -and we register this operation in the `ToyDialect` constructor: +and we register this operation in the `ToyDialect` initializer: ```c++ -ToyDialect::ToyDialect(mlir::MLIRContext *ctx) - : mlir::Dialect(getDialectNamespace(), ctx) { +void ToyDialect::initialize() { addOperations(); } ``` @@ -311,27 +340,9 @@ Lets see how to define the ODS equivalent of our ConstantOp: -The first thing to do is to define a link to the Toy dialect that we defined in -C++. This is used to link all of the operations that we will define to our -dialect: - -```tablegen -// Provide a definition of the 'toy' dialect in the ODS framework so that we -// can define our operations. -def Toy_Dialect : Dialect { - // The namespace of our dialect, this corresponds 1-1 with the string we - // provided in `ToyDialect::getDialectNamespace`. - let name = "toy"; - - // The C++ namespace that the dialect class definition resides in. - let cppNamespace = "toy"; -} -``` - -Now that we have defined a link to the Toy dialect, we can start defining -operations. Operations in ODS are defined by inheriting from the `Op` class. To -simplify our operation definitions, we will define a base class for operations -in the Toy dialect. +Operations in ODS are defined by inheriting from the `Op` class. To simplify our +operation definitions, we will define a base class for operations in the Toy +dialect. ```tablegen // Base class for toy dialect operations. This operation inherits from the base diff --git a/mlir/docs/Tutorials/Toy/Ch-4.md b/mlir/docs/Tutorials/Toy/Ch-4.md --- a/mlir/docs/Tutorials/Toy/Ch-4.md +++ b/mlir/docs/Tutorials/Toy/Ch-4.md @@ -99,7 +99,7 @@ how we did for operations. ```c++ -ToyDialect::ToyDialect(mlir::MLIRContext *ctx) : mlir::Dialect("toy", ctx) { +void ToyDialect::initialize() { addInterfaces(); } ``` diff --git a/mlir/docs/Tutorials/Toy/Ch-7.md b/mlir/docs/Tutorials/Toy/Ch-7.md --- a/mlir/docs/Tutorials/Toy/Ch-7.md +++ b/mlir/docs/Tutorials/Toy/Ch-7.md @@ -177,12 +177,11 @@ }; ``` -We register this type in the `ToyDialect` constructor in a similar way to how we +We register this type in the `ToyDialect` initializer in a similar way to how we did with operations: ```c++ -ToyDialect::ToyDialect(mlir::MLIRContext *ctx) - : mlir::Dialect(getDialectNamespace(), ctx) { +void ToyDialect::initialize() { addTypes(); } ``` @@ -193,12 +192,32 @@ With this we can now use our `StructType` when generating MLIR from Toy. See examples/toy/Ch7/mlir/MLIRGen.cpp for more details. +### Exposing to ODS + +After defining a new type, we should make the ODS framework aware of our Type so +that we can use it in the operation definitions and auto-generate utilities +within the Dialect. A simple example is shown below: + +```tablegen +// Provide a definition for the Toy StructType for use in ODS. This allows for +// using StructType in a similar way to Tensor or MemRef. We use `DialectType` +// to demarcate the StructType as belonging to the Toy dialect. +def Toy_StructType : + DialectType()">, + "Toy struct type">; + +// Provide a definition of the types that are used within the Toy dialect. +def Toy_Type : AnyTypeOf<[F64Tensor, Toy_StructType]>; +``` + ### Parsing and Printing At this point we can use our `StructType` during MLIR generation and transformation, but we can't output or parse `.mlir`. For this we need to add support for parsing and printing instances of the `StructType`. This can be done by overriding the `parseType` and `printType` methods on the `ToyDialect`. +Declarations for these methods are automatically provided when the type is +exposed to ODS as detailed in the previous section. ```c++ class ToyDialect : public mlir::Dialect { @@ -321,22 +340,8 @@ #### Updating Existing Operations -A few of our existing operations will need to be updated to handle `StructType`. -The first step is to make the ODS framework aware of our Type so that we can use -it in the operation definitions. A simple example is shown below: - -```tablegen -// Provide a definition for the Toy StructType for use in ODS. This allows for -// using StructType in a similar way to Tensor or MemRef. -def Toy_StructType : - Type()">, "Toy struct type">; - -// Provide a definition of the types that are used within the Toy dialect. -def Toy_Type : AnyTypeOf<[F64Tensor, Toy_StructType]>; -``` - -We can then update our operations, e.g. `ReturnOp`, to also accept the -`Toy_StructType`: +A few of our existing operations, e.g. `ReturnOp`, will need to be updated to +handle `Toy_StructType`. ```tablegen def ReturnOp : Toy_Op<"return", [Terminator, HasParent<"FuncOp">]> { diff --git a/mlir/examples/toy/Ch2/include/toy/CMakeLists.txt b/mlir/examples/toy/Ch2/include/toy/CMakeLists.txt --- a/mlir/examples/toy/Ch2/include/toy/CMakeLists.txt +++ b/mlir/examples/toy/Ch2/include/toy/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_TARGET_DEFINITIONS Ops.td) mlir_tablegen(Ops.h.inc -gen-op-decls) mlir_tablegen(Ops.cpp.inc -gen-op-defs) +mlir_tablegen(Dialect.h.inc -gen-dialect-decls) add_public_tablegen_target(ToyCh2OpsIncGen) diff --git a/mlir/examples/toy/Ch2/include/toy/Dialect.h b/mlir/examples/toy/Ch2/include/toy/Dialect.h --- a/mlir/examples/toy/Ch2/include/toy/Dialect.h +++ b/mlir/examples/toy/Ch2/include/toy/Dialect.h @@ -18,24 +18,9 @@ #include "mlir/IR/Dialect.h" #include "mlir/Interfaces/SideEffectInterfaces.h" -namespace mlir { -namespace toy { - -/// This is the definition of the Toy dialect. A dialect inherits from -/// mlir::Dialect and registers custom attributes, operations, and types (in its -/// constructor). It can also override some general behavior exposed via virtual -/// methods. -class ToyDialect : public mlir::Dialect { -public: - explicit ToyDialect(mlir::MLIRContext *ctx); - - /// Provide a utility accessor to the dialect namespace. This is used by - /// several utilities for casting between dialects. - static llvm::StringRef getDialectNamespace() { return "toy"; } -}; - -} // end namespace toy -} // end namespace mlir +/// Include the auto-generated header file containing the declaration of the toy +/// dialect. +#include "toy/Dialect.h.inc" /// Include the auto-generated header file containing the declarations of the /// toy operations. diff --git a/mlir/examples/toy/Ch2/mlir/Dialect.cpp b/mlir/examples/toy/Ch2/mlir/Dialect.cpp --- a/mlir/examples/toy/Ch2/mlir/Dialect.cpp +++ b/mlir/examples/toy/Ch2/mlir/Dialect.cpp @@ -24,10 +24,9 @@ // ToyDialect //===----------------------------------------------------------------------===// -/// Dialect creation, the instance will be owned by the context. This is the -/// point of registration of custom types and operations for the dialect. -ToyDialect::ToyDialect(mlir::MLIRContext *ctx) - : mlir::Dialect(getDialectNamespace(), ctx, TypeID::get()) { +/// Dialect initialization, the instance will be owned by the context. This is +/// the point of registration of types and operations for the dialect. +void ToyDialect::initialize() { addOperations< #define GET_OP_LIST #include "toy/Ops.cpp.inc" diff --git a/mlir/examples/toy/Ch3/include/toy/CMakeLists.txt b/mlir/examples/toy/Ch3/include/toy/CMakeLists.txt --- a/mlir/examples/toy/Ch3/include/toy/CMakeLists.txt +++ b/mlir/examples/toy/Ch3/include/toy/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_TARGET_DEFINITIONS Ops.td) mlir_tablegen(Ops.h.inc -gen-op-decls) mlir_tablegen(Ops.cpp.inc -gen-op-defs) +mlir_tablegen(Dialect.h.inc -gen-dialect-decls) add_public_tablegen_target(ToyCh3OpsIncGen) diff --git a/mlir/examples/toy/Ch3/include/toy/Dialect.h b/mlir/examples/toy/Ch3/include/toy/Dialect.h --- a/mlir/examples/toy/Ch3/include/toy/Dialect.h +++ b/mlir/examples/toy/Ch3/include/toy/Dialect.h @@ -18,24 +18,9 @@ #include "mlir/IR/Dialect.h" #include "mlir/Interfaces/SideEffectInterfaces.h" -namespace mlir { -namespace toy { - -/// This is the definition of the Toy dialect. A dialect inherits from -/// mlir::Dialect and registers custom attributes, operations, and types (in its -/// constructor). It can also override some general behavior exposed via virtual -/// methods. -class ToyDialect : public mlir::Dialect { -public: - explicit ToyDialect(mlir::MLIRContext *ctx); - - /// Provide a utility accessor to the dialect namespace. This is used by - /// several utilities for casting between dialects. - static llvm::StringRef getDialectNamespace() { return "toy"; } -}; - -} // end namespace toy -} // end namespace mlir +/// Include the auto-generated header file containing the declaration of the toy +/// dialect. +#include "toy/Dialect.h.inc" /// Include the auto-generated header file containing the declarations of the /// toy operations. diff --git a/mlir/examples/toy/Ch3/mlir/Dialect.cpp b/mlir/examples/toy/Ch3/mlir/Dialect.cpp --- a/mlir/examples/toy/Ch3/mlir/Dialect.cpp +++ b/mlir/examples/toy/Ch3/mlir/Dialect.cpp @@ -24,10 +24,9 @@ // ToyDialect //===----------------------------------------------------------------------===// -/// Dialect creation, the instance will be owned by the context. This is the -/// point of registration of custom types and operations for the dialect. -ToyDialect::ToyDialect(mlir::MLIRContext *ctx) - : mlir::Dialect(getDialectNamespace(), ctx, TypeID::get()) { +/// Dialect initialization, the instance will be owned by the context. This is +/// the point of registration of types and operations for the dialect. +void ToyDialect::initialize() { addOperations< #define GET_OP_LIST #include "toy/Ops.cpp.inc" diff --git a/mlir/examples/toy/Ch4/include/toy/CMakeLists.txt b/mlir/examples/toy/Ch4/include/toy/CMakeLists.txt --- a/mlir/examples/toy/Ch4/include/toy/CMakeLists.txt +++ b/mlir/examples/toy/Ch4/include/toy/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_TARGET_DEFINITIONS Ops.td) mlir_tablegen(Ops.h.inc -gen-op-decls) mlir_tablegen(Ops.cpp.inc -gen-op-defs) +mlir_tablegen(Dialect.h.inc -gen-dialect-decls) add_public_tablegen_target(ToyCh4OpsIncGen) # Most dialects should use add_mlir_interfaces(). diff --git a/mlir/examples/toy/Ch4/include/toy/Dialect.h b/mlir/examples/toy/Ch4/include/toy/Dialect.h --- a/mlir/examples/toy/Ch4/include/toy/Dialect.h +++ b/mlir/examples/toy/Ch4/include/toy/Dialect.h @@ -21,24 +21,9 @@ #include "mlir/Interfaces/SideEffectInterfaces.h" #include "toy/ShapeInferenceInterface.h" -namespace mlir { -namespace toy { - -/// This is the definition of the Toy dialect. A dialect inherits from -/// mlir::Dialect and registers custom attributes, operations, and types (in its -/// constructor). It can also override some general behavior exposed via virtual -/// methods. -class ToyDialect : public mlir::Dialect { -public: - explicit ToyDialect(mlir::MLIRContext *ctx); - - /// Provide a utility accessor to the dialect namespace. This is used by - /// several utilities for casting between dialects. - static llvm::StringRef getDialectNamespace() { return "toy"; } -}; - -} // end namespace toy -} // end namespace mlir +/// Include the auto-generated header file containing the declaration of the toy +/// dialect. +#include "toy/Dialect.h.inc" /// Include the auto-generated header file containing the declarations of the /// toy operations. diff --git a/mlir/examples/toy/Ch4/mlir/Dialect.cpp b/mlir/examples/toy/Ch4/mlir/Dialect.cpp --- a/mlir/examples/toy/Ch4/mlir/Dialect.cpp +++ b/mlir/examples/toy/Ch4/mlir/Dialect.cpp @@ -79,10 +79,9 @@ // ToyDialect //===----------------------------------------------------------------------===// -/// Dialect creation, the instance will be owned by the context. This is the -/// point of registration of custom types and operations for the dialect. -ToyDialect::ToyDialect(mlir::MLIRContext *ctx) - : mlir::Dialect(getDialectNamespace(), ctx, TypeID::get()) { +/// Dialect initialization, the instance will be owned by the context. This is +/// the point of registration of types and operations for the dialect. +void ToyDialect::initialize() { addOperations< #define GET_OP_LIST #include "toy/Ops.cpp.inc" diff --git a/mlir/examples/toy/Ch5/include/toy/CMakeLists.txt b/mlir/examples/toy/Ch5/include/toy/CMakeLists.txt --- a/mlir/examples/toy/Ch5/include/toy/CMakeLists.txt +++ b/mlir/examples/toy/Ch5/include/toy/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_TARGET_DEFINITIONS Ops.td) mlir_tablegen(Ops.h.inc -gen-op-decls) mlir_tablegen(Ops.cpp.inc -gen-op-defs) +mlir_tablegen(Dialect.h.inc -gen-dialect-decls) add_public_tablegen_target(ToyCh5OpsIncGen) # Most dialects should use add_mlir_interfaces(). diff --git a/mlir/examples/toy/Ch5/include/toy/Dialect.h b/mlir/examples/toy/Ch5/include/toy/Dialect.h --- a/mlir/examples/toy/Ch5/include/toy/Dialect.h +++ b/mlir/examples/toy/Ch5/include/toy/Dialect.h @@ -21,24 +21,9 @@ #include "mlir/Interfaces/SideEffectInterfaces.h" #include "toy/ShapeInferenceInterface.h" -namespace mlir { -namespace toy { - -/// This is the definition of the Toy dialect. A dialect inherits from -/// mlir::Dialect and registers custom attributes, operations, and types (in its -/// constructor). It can also override some general behavior exposed via virtual -/// methods. -class ToyDialect : public mlir::Dialect { -public: - explicit ToyDialect(mlir::MLIRContext *ctx); - - /// Provide a utility accessor to the dialect namespace. This is used by - /// several utilities for casting between dialects. - static llvm::StringRef getDialectNamespace() { return "toy"; } -}; - -} // end namespace toy -} // end namespace mlir +/// Include the auto-generated header file containing the declaration of the toy +/// dialect. +#include "toy/Dialect.h.inc" /// Include the auto-generated header file containing the declarations of the /// toy operations. diff --git a/mlir/examples/toy/Ch5/mlir/Dialect.cpp b/mlir/examples/toy/Ch5/mlir/Dialect.cpp --- a/mlir/examples/toy/Ch5/mlir/Dialect.cpp +++ b/mlir/examples/toy/Ch5/mlir/Dialect.cpp @@ -79,10 +79,9 @@ // ToyDialect //===----------------------------------------------------------------------===// -/// Dialect creation, the instance will be owned by the context. This is the -/// point of registration of custom types and operations for the dialect. -ToyDialect::ToyDialect(mlir::MLIRContext *ctx) - : mlir::Dialect(getDialectNamespace(), ctx, TypeID::get()) { +/// Dialect initialization, the instance will be owned by the context. This is +/// the point of registration of types and operations for the dialect. +void ToyDialect::initialize() { addOperations< #define GET_OP_LIST #include "toy/Ops.cpp.inc" diff --git a/mlir/examples/toy/Ch6/include/toy/CMakeLists.txt b/mlir/examples/toy/Ch6/include/toy/CMakeLists.txt --- a/mlir/examples/toy/Ch6/include/toy/CMakeLists.txt +++ b/mlir/examples/toy/Ch6/include/toy/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_TARGET_DEFINITIONS Ops.td) mlir_tablegen(Ops.h.inc -gen-op-decls) mlir_tablegen(Ops.cpp.inc -gen-op-defs) +mlir_tablegen(Dialect.h.inc -gen-dialect-decls) add_public_tablegen_target(ToyCh6OpsIncGen) # Most dialects should use add_mlir_interfaces(). diff --git a/mlir/examples/toy/Ch6/include/toy/Dialect.h b/mlir/examples/toy/Ch6/include/toy/Dialect.h --- a/mlir/examples/toy/Ch6/include/toy/Dialect.h +++ b/mlir/examples/toy/Ch6/include/toy/Dialect.h @@ -21,24 +21,9 @@ #include "mlir/Interfaces/SideEffectInterfaces.h" #include "toy/ShapeInferenceInterface.h" -namespace mlir { -namespace toy { - -/// This is the definition of the Toy dialect. A dialect inherits from -/// mlir::Dialect and registers custom attributes, operations, and types (in its -/// constructor). It can also override some general behavior exposed via virtual -/// methods. -class ToyDialect : public mlir::Dialect { -public: - explicit ToyDialect(mlir::MLIRContext *ctx); - - /// Provide a utility accessor to the dialect namespace. This is used by - /// several utilities for casting between dialects. - static llvm::StringRef getDialectNamespace() { return "toy"; } -}; - -} // end namespace toy -} // end namespace mlir +/// Include the auto-generated header file containing the declaration of the toy +/// dialect. +#include "toy/Dialect.h.inc" /// Include the auto-generated header file containing the declarations of the /// toy operations. diff --git a/mlir/examples/toy/Ch6/mlir/Dialect.cpp b/mlir/examples/toy/Ch6/mlir/Dialect.cpp --- a/mlir/examples/toy/Ch6/mlir/Dialect.cpp +++ b/mlir/examples/toy/Ch6/mlir/Dialect.cpp @@ -79,10 +79,9 @@ // ToyDialect //===----------------------------------------------------------------------===// -/// Dialect creation, the instance will be owned by the context. This is the -/// point of registration of custom types and operations for the dialect. -ToyDialect::ToyDialect(mlir::MLIRContext *ctx) - : mlir::Dialect(getDialectNamespace(), ctx, TypeID::get()) { +/// Dialect initialization, the instance will be owned by the context. This is +/// the point of registration of types and operations for the dialect. +void ToyDialect::initialize() { addOperations< #define GET_OP_LIST #include "toy/Ops.cpp.inc" diff --git a/mlir/examples/toy/Ch7/include/toy/CMakeLists.txt b/mlir/examples/toy/Ch7/include/toy/CMakeLists.txt --- a/mlir/examples/toy/Ch7/include/toy/CMakeLists.txt +++ b/mlir/examples/toy/Ch7/include/toy/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_TARGET_DEFINITIONS Ops.td) mlir_tablegen(Ops.h.inc -gen-op-decls) mlir_tablegen(Ops.cpp.inc -gen-op-defs) +mlir_tablegen(Dialect.h.inc -gen-dialect-decls) add_public_tablegen_target(ToyCh7OpsIncGen) # Most dialects should use add_mlir_interfaces(). diff --git a/mlir/examples/toy/Ch7/include/toy/Dialect.h b/mlir/examples/toy/Ch7/include/toy/Dialect.h --- a/mlir/examples/toy/Ch7/include/toy/Dialect.h +++ b/mlir/examples/toy/Ch7/include/toy/Dialect.h @@ -26,34 +26,13 @@ namespace detail { struct StructTypeStorage; } // end namespace detail - -/// This is the definition of the Toy dialect. A dialect inherits from -/// mlir::Dialect and registers custom attributes, operations, and types (in its -/// constructor). It can also override some general behavior exposed via virtual -/// methods. -class ToyDialect : public mlir::Dialect { -public: - explicit ToyDialect(mlir::MLIRContext *ctx); - - /// A hook used to materialize constant values with the given type. - Operation *materializeConstant(OpBuilder &builder, Attribute value, Type type, - Location loc) override; - - /// Parse an instance of a type registered to the toy dialect. - mlir::Type parseType(mlir::DialectAsmParser &parser) const override; - - /// Print an instance of a type registered to the toy dialect. - void printType(mlir::Type type, - mlir::DialectAsmPrinter &printer) const override; - - /// Provide a utility accessor to the dialect namespace. This is used by - /// several utilities for casting between dialects. - static llvm::StringRef getDialectNamespace() { return "toy"; } -}; - } // end namespace toy } // end namespace mlir +/// Include the auto-generated header file containing the declaration of the toy +/// dialect. +#include "toy/Dialect.h.inc" + //===----------------------------------------------------------------------===// // Toy Operations //===----------------------------------------------------------------------===// diff --git a/mlir/examples/toy/Ch7/include/toy/Ops.td b/mlir/examples/toy/Ch7/include/toy/Ops.td --- a/mlir/examples/toy/Ch7/include/toy/Ops.td +++ b/mlir/examples/toy/Ch7/include/toy/Ops.td @@ -23,6 +23,10 @@ def Toy_Dialect : Dialect { let name = "toy"; let cppNamespace = "::mlir::toy"; + + // We set this bit to generate a declaration of the `materializeConstant` + // method so that we can materialize constants for our toy operations. + let hasConstantMaterializer = 1; } // Base class for toy dialect operations. This operation inherits from the base @@ -34,9 +38,11 @@ Op; // Provide a definition for the Toy StructType for use in ODS. This allows for -// using StructType in a similar way to Tensor or MemRef. +// using StructType in a similar way to Tensor or MemRef. We use `DialectType` +// to demarcate the StructType as belonging to the Toy dialect. def Toy_StructType : - Type()">, "Toy struct type">; + DialectType()">, + "Toy struct type">; // Provide a definition of the types that are used within the Toy dialect. def Toy_Type : AnyTypeOf<[F64Tensor, Toy_StructType]>; diff --git a/mlir/examples/toy/Ch7/mlir/Dialect.cpp b/mlir/examples/toy/Ch7/mlir/Dialect.cpp --- a/mlir/examples/toy/Ch7/mlir/Dialect.cpp +++ b/mlir/examples/toy/Ch7/mlir/Dialect.cpp @@ -544,10 +544,9 @@ // ToyDialect //===----------------------------------------------------------------------===// -/// Dialect creation, the instance will be owned by the context. This is the -/// point of registration of custom types and operations for the dialect. -ToyDialect::ToyDialect(mlir::MLIRContext *ctx) - : mlir::Dialect(getDialectNamespace(), ctx, TypeID::get()) { +/// Dialect initialization, the instance will be owned by the context. This is +/// the point of registration of types and operations for the dialect. +void ToyDialect::initialize() { addOperations< #define GET_OP_LIST #include "toy/Ops.cpp.inc"