xref: /llvm-project/mlir/docs/Tutorials/Toy/Ch-2.md (revision 9d0fa42fbbffe3ff584b26f3a48f8f786f68da72)
15b4a01d4SMehdi Amini# Chapter 2: Emitting Basic MLIR
25b4a01d4SMehdi Amini
35b4a01d4SMehdi Amini[TOC]
45b4a01d4SMehdi Amini
55b4a01d4SMehdi AminiNow that we're familiar with our language and the AST, let's see how MLIR can
65b4a01d4SMehdi Aminihelp to compile Toy.
75b4a01d4SMehdi Amini
85b4a01d4SMehdi Amini## Introduction: Multi-Level Intermediate Representation
95b4a01d4SMehdi Amini
105b4a01d4SMehdi AminiOther compilers, like LLVM (see the
115b4a01d4SMehdi Amini[Kaleidoscope tutorial](https://llvm.org/docs/tutorial/MyFirstLanguageFrontend/index.html)),
125b4a01d4SMehdi Aminioffer a fixed set of predefined types and (usually *low-level* / RISC-like)
135b4a01d4SMehdi Aminiinstructions. It is up to the frontend for a given language to perform any
145b4a01d4SMehdi Aminilanguage-specific type-checking, analysis, or transformation before emitting
155b4a01d4SMehdi AminiLLVM IR. For example, Clang will use its AST to perform not only static analysis
165b4a01d4SMehdi Aminibut also transformations, such as C++ template instantiation through AST cloning
175b4a01d4SMehdi Aminiand rewrite. Finally, languages with construction at a higher-level than C/C++
185b4a01d4SMehdi Aminimay require non-trivial lowering from their AST to generate LLVM IR.
195b4a01d4SMehdi Amini
205b4a01d4SMehdi AminiAs a consequence, multiple frontends end up reimplementing significant pieces of
215b4a01d4SMehdi Aminiinfrastructure to support the need for these analyses and transformation. MLIR
225b4a01d4SMehdi Aminiaddresses this issue by being designed for extensibility. As such, there are few
235b4a01d4SMehdi Aminipre-defined instructions (*operations* in MLIR terminology) or types.
245b4a01d4SMehdi Amini
255b4a01d4SMehdi Amini## Interfacing with MLIR
265b4a01d4SMehdi Amini
275a8d5a28SRiver Riddle[Language Reference](../../LangRef.md)
285b4a01d4SMehdi Amini
295b4a01d4SMehdi AminiMLIR is designed to be a completely extensible infrastructure; there is no
305b4a01d4SMehdi Aminiclosed set of attributes (think: constant metadata), operations, or types. MLIR
315b4a01d4SMehdi Aminisupports this extensibility with the concept of
3231d1ae79SMarkus Böck[Dialects](../../LangRef.md/#dialects). Dialects provide a grouping mechanism for
335b4a01d4SMehdi Aminiabstraction under a unique `namespace`.
345b4a01d4SMehdi Amini
3531d1ae79SMarkus BöckIn MLIR, [`Operations`](../../LangRef.md/#operations) are the core unit of
365b4a01d4SMehdi Aminiabstraction and computation, similar in many ways to LLVM instructions.
375b4a01d4SMehdi AminiOperations can have application-specific semantics and can be used to represent
385b4a01d4SMehdi Aminiall of the core IR structures in LLVM: instructions, globals (like functions),
395b4a01d4SMehdi Aminimodules, etc.
405b4a01d4SMehdi Amini
415b4a01d4SMehdi AminiHere is the MLIR assembly for the Toy `transpose` operations:
425b4a01d4SMehdi Amini
435b4a01d4SMehdi Amini```mlir
445b4a01d4SMehdi Amini%t_tensor = "toy.transpose"(%tensor) {inplace = true} : (tensor<2x3xf64>) -> tensor<3x2xf64> loc("example/file/path":12:1)
455b4a01d4SMehdi Amini```
465b4a01d4SMehdi Amini
475b4a01d4SMehdi AminiLet's break down the anatomy of this MLIR operation:
485b4a01d4SMehdi Amini
495b4a01d4SMehdi Amini-   `%t_tensor`
505b4a01d4SMehdi Amini
515b4a01d4SMehdi Amini    *   The name given to the result defined by this operation (which includes
5231d1ae79SMarkus Böck        [a prefixed sigil to avoid collisions](../../LangRef.md/#identifiers-and-keywords)).
535b4a01d4SMehdi Amini        An operation may define zero or more results (in the context of Toy, we
545b4a01d4SMehdi Amini        will limit ourselves to single-result operations), which are SSA values.
555b4a01d4SMehdi Amini        The name is used during parsing but is not persistent (e.g., it is not
565b4a01d4SMehdi Amini        tracked in the in-memory representation of the SSA value).
575b4a01d4SMehdi Amini
585b4a01d4SMehdi Amini-   `"toy.transpose"`
595b4a01d4SMehdi Amini
605b4a01d4SMehdi Amini    *   The name of the operation. It is expected to be a unique string, with
615b4a01d4SMehdi Amini        the namespace of the dialect prefixed before the "`.`". This can be read
625b4a01d4SMehdi Amini        as the `transpose` operation in the `toy` dialect.
635b4a01d4SMehdi Amini
645b4a01d4SMehdi Amini-   `(%tensor)`
655b4a01d4SMehdi Amini
665b4a01d4SMehdi Amini    *   A list of zero or more input operands (or arguments), which are SSA
675b4a01d4SMehdi Amini        values defined by other operations or referring to block arguments.
685b4a01d4SMehdi Amini
695b4a01d4SMehdi Amini-   `{ inplace = true }`
705b4a01d4SMehdi Amini
715b4a01d4SMehdi Amini    *   A dictionary of zero or more attributes, which are special operands that
725b4a01d4SMehdi Amini        are always constant. Here we define a boolean attribute named 'inplace'
735b4a01d4SMehdi Amini        that has a constant value of true.
745b4a01d4SMehdi Amini
755b4a01d4SMehdi Amini-   `(tensor<2x3xf64>) -> tensor<3x2xf64>`
765b4a01d4SMehdi Amini
775b4a01d4SMehdi Amini    *   This refers to the type of the operation in a functional form, spelling
785b4a01d4SMehdi Amini        the types of the arguments in parentheses and the type of the return
795b4a01d4SMehdi Amini        values afterward.
805b4a01d4SMehdi Amini
815b4a01d4SMehdi Amini-   `loc("example/file/path":12:1)`
825b4a01d4SMehdi Amini
835b4a01d4SMehdi Amini    *   This is the location in the source code from which this operation
845b4a01d4SMehdi Amini        originated.
855b4a01d4SMehdi Amini
862e628d00SStephen NeuendorfferShown here is the general form of an operation. As described above,
872e628d00SStephen Neuendorfferthe set of operations in MLIR is extensible. Operations are modeled
882e628d00SStephen Neuendorfferusing a small set of concepts, enabling operations to be reasoned
892e628d00SStephen Neuendorfferabout and manipulated generically. These concepts are:
905b4a01d4SMehdi Amini
915b4a01d4SMehdi Amini-   A name for the operation.
925b4a01d4SMehdi Amini-   A list of SSA operand values.
9331d1ae79SMarkus Böck-   A list of [attributes](../../LangRef.md/#attributes).
9431d1ae79SMarkus Böck-   A list of [types](../../LangRef.md/#type-system) for result values.
9531d1ae79SMarkus Böck-   A [source location](../../Diagnostics.md/#source-locations) for debugging
965b4a01d4SMehdi Amini    purposes.
9731d1ae79SMarkus Böck-   A list of successors [blocks](../../LangRef.md/#blocks) (for branches,
985b4a01d4SMehdi Amini    mostly).
9931d1ae79SMarkus Böck-   A list of [regions](../../LangRef.md/#regions) (for structural operations
1005b4a01d4SMehdi Amini    like functions).
1015b4a01d4SMehdi Amini
1025b4a01d4SMehdi AminiIn MLIR, every operation has a mandatory source location associated with it.
1035b4a01d4SMehdi AminiContrary to LLVM, where debug info locations are metadata and can be dropped, in
1045b4a01d4SMehdi AminiMLIR, the location is a core requirement, and APIs depend on and manipulate it.
1055b4a01d4SMehdi AminiDropping a location is thus an explicit choice which cannot happen by mistake.
1065b4a01d4SMehdi Amini
1075b4a01d4SMehdi AminiTo provide an illustration: If a transformation replaces an operation by
1085b4a01d4SMehdi Aminianother, that new operation must still have a location attached. This makes it
1095b4a01d4SMehdi Aminipossible to track where that operation came from.
1105b4a01d4SMehdi Amini
1115b4a01d4SMehdi AminiIt's worth noting that the mlir-opt tool - a tool for testing
1125b4a01d4SMehdi Aminicompiler passes - does not include locations in the output by default. The
1135b4a01d4SMehdi Amini`-mlir-print-debuginfo` flag specifies to include locations. (Run `mlir-opt
1145b4a01d4SMehdi Amini--help` for more options.)
1155b4a01d4SMehdi Amini
1165b4a01d4SMehdi Amini### Opaque API
1175b4a01d4SMehdi Amini
1185a8d5a28SRiver RiddleMLIR is designed to allow all IR elements, such as attributes, operations, and
1195a8d5a28SRiver Riddletypes, to be customized. At the same time, IR elements can always be reduced to
1205a8d5a28SRiver Riddlethe above fundamental concepts. This allows MLIR to parse, represent, and
12131d1ae79SMarkus Böck[round-trip](../../../getting_started/Glossary.md/#round-trip) IR for *any*
1225a8d5a28SRiver Riddleoperation. For example, we could place our Toy operation from above into an
123ee2c6cd9SRiver Riddle`.mlir` file and round-trip through *mlir-opt* without registering any `toy`
124ee2c6cd9SRiver Riddlerelated dialect:
1255b4a01d4SMehdi Amini
1265b4a01d4SMehdi Amini```mlir
1272310ced8SRiver Riddlefunc.func @toy_func(%tensor: tensor<2x3xf64>) -> tensor<3x2xf64> {
1285b4a01d4SMehdi Amini  %t_tensor = "toy.transpose"(%tensor) { inplace = true } : (tensor<2x3xf64>) -> tensor<3x2xf64>
1295b4a01d4SMehdi Amini  return %t_tensor : tensor<3x2xf64>
1305b4a01d4SMehdi Amini}
1315b4a01d4SMehdi Amini```
1325b4a01d4SMehdi Amini
1335a8d5a28SRiver RiddleIn the cases of unregistered attributes, operations, and types, MLIR will
1345a8d5a28SRiver Riddleenforce some structural constraints (e.g. dominance, etc.), but otherwise they
1355a8d5a28SRiver Riddleare completely opaque. For instance, MLIR has little information about whether
1365a8d5a28SRiver Riddlean unregistered operation can operate on particular data types, how many
1375a8d5a28SRiver Riddleoperands it can take, or how many results it produces. This flexibility can be
1385a8d5a28SRiver Riddleuseful for bootstrapping purposes, but it is generally advised against in mature
1392e628d00SStephen Neuendorffersystems. Unregistered operations must be treated conservatively by
1405a8d5a28SRiver Riddletransformations and analyses, and they are much harder to construct and
1415a8d5a28SRiver Riddlemanipulate.
1425b4a01d4SMehdi Amini
1435b4a01d4SMehdi AminiThis handling can be observed by crafting what should be an invalid IR for Toy
1445b4a01d4SMehdi Aminiand seeing it round-trip without tripping the verifier:
1455b4a01d4SMehdi Amini
1465b4a01d4SMehdi Amini```mlir
1472310ced8SRiver Riddlefunc.func @main() {
1485b4a01d4SMehdi Amini  %0 = "toy.print"() : () -> tensor<2x3xf64>
1495b4a01d4SMehdi Amini}
1505b4a01d4SMehdi Amini```
1515b4a01d4SMehdi Amini
1525b4a01d4SMehdi AminiThere are multiple problems here: the `toy.print` operation is not a terminator;
1535b4a01d4SMehdi Aminiit should take an operand; and it shouldn't return any values. In the next
1545b4a01d4SMehdi Aminisection, we will register our dialect and operations with MLIR, plug into the
1555b4a01d4SMehdi Aminiverifier, and add nicer APIs to manipulate our operations.
1565b4a01d4SMehdi Amini
1575b4a01d4SMehdi Amini## Defining a Toy Dialect
1585b4a01d4SMehdi Amini
1595b4a01d4SMehdi AminiTo effectively interface with MLIR, we will define a new Toy dialect. This
1605a8d5a28SRiver Riddledialect will model the structure of the Toy language, as well as provide an easy
1615a8d5a28SRiver Riddleavenue for high-level analysis and transformation.
1625b4a01d4SMehdi Amini
1635b4a01d4SMehdi Amini```c++
1645b4a01d4SMehdi Amini/// This is the definition of the Toy dialect. A dialect inherits from
1655a8d5a28SRiver Riddle/// mlir::Dialect and registers custom attributes, operations, and types. It can
1665a8d5a28SRiver Riddle/// also override virtual methods to change some general behavior, which will be
1675a8d5a28SRiver Riddle/// demonstrated in later chapters of the tutorial.
1685b4a01d4SMehdi Aminiclass ToyDialect : public mlir::Dialect {
1695b4a01d4SMehdi Aminipublic:
1705b4a01d4SMehdi Amini  explicit ToyDialect(mlir::MLIRContext *ctx);
1715b4a01d4SMehdi Amini
1725a8d5a28SRiver Riddle  /// Provide a utility accessor to the dialect namespace.
1735b4a01d4SMehdi Amini  static llvm::StringRef getDialectNamespace() { return "toy"; }
174ee748605SRiver Riddle
175ee748605SRiver Riddle  /// An initializer called from the constructor of ToyDialect that is used to
1765a8d5a28SRiver Riddle  /// register attributes, operations, types, and more within the Toy dialect.
177ee748605SRiver Riddle  void initialize();
1785b4a01d4SMehdi Amini};
1795b4a01d4SMehdi Amini```
1805b4a01d4SMehdi Amini
181ee748605SRiver RiddleThis is the C++ definition of a dialect, but MLIR also supports defining
1825a8d5a28SRiver Riddledialects declaratively via
1835a8d5a28SRiver Riddle[tablegen](https://llvm.org/docs/TableGen/ProgRef.html). Using the declarative
1845a8d5a28SRiver Riddlespecification is much cleaner as it removes the need for a large portion of the
1855a8d5a28SRiver Riddleboilerplate when defining a new dialect. It also enables easy generation of
1865a8d5a28SRiver Riddledialect documentation, which can be described directly alongside the dialect. In
1875a8d5a28SRiver Riddlethis declarative format, the toy dialect would be specified as:
188ee748605SRiver Riddle
189ee748605SRiver Riddle```tablegen
190ee748605SRiver Riddle// Provide a definition of the 'toy' dialect in the ODS framework so that we
191ee748605SRiver Riddle// can define our operations.
192ee748605SRiver Riddledef Toy_Dialect : Dialect {
193ee748605SRiver Riddle  // The namespace of our dialect, this corresponds 1-1 with the string we
194ee748605SRiver Riddle  // provided in `ToyDialect::getDialectNamespace`.
195ee748605SRiver Riddle  let name = "toy";
196ee748605SRiver Riddle
1975a8d5a28SRiver Riddle  // A short one-line summary of our dialect.
1985a8d5a28SRiver Riddle  let summary = "A high-level dialect for analyzing and optimizing the "
1995a8d5a28SRiver Riddle                "Toy language";
2005a8d5a28SRiver Riddle
2015a8d5a28SRiver Riddle  // A much longer description of our dialect.
2025a8d5a28SRiver Riddle  let description = [{
2035a8d5a28SRiver Riddle    The Toy language is a tensor-based language that allows you to define
2045a8d5a28SRiver Riddle    functions, perform some math computation, and print results. This dialect
2055a8d5a28SRiver Riddle    provides a representation of the language that is amenable to analysis and
2065a8d5a28SRiver Riddle    optimization.
2075a8d5a28SRiver Riddle  }];
2085a8d5a28SRiver Riddle
209ee748605SRiver Riddle  // The C++ namespace that the dialect class definition resides in.
210ee748605SRiver Riddle  let cppNamespace = "toy";
211ee748605SRiver Riddle}
212ee748605SRiver Riddle```
213ee748605SRiver Riddle
214ee748605SRiver RiddleTo see what this generates, we can run the `mlir-tblgen` command with the
215ee748605SRiver Riddle`gen-dialect-decls` action like so:
216ee748605SRiver Riddle
217ee748605SRiver Riddle```shell
218ee748605SRiver Riddle${build_root}/bin/mlir-tblgen -gen-dialect-decls ${mlir_src_root}/examples/toy/Ch2/include/toy/Ops.td -I ${mlir_src_root}/include/
219ee748605SRiver Riddle```
220ee748605SRiver Riddle
2215a8d5a28SRiver RiddleAfter the dialect has been defined, it can now be loaded into an MLIRContext:
2225b4a01d4SMehdi Amini
2235b4a01d4SMehdi Amini```c++
224ee748605SRiver Riddle  context.loadDialect<ToyDialect>();
2255b4a01d4SMehdi Amini```
2265b4a01d4SMehdi Amini
2275a8d5a28SRiver RiddleBy default, an `MLIRContext` only loads the
2285a8d5a28SRiver Riddle[Builtin Dialect](../../Dialects/Builtin.md), which provides a few core IR
2295a8d5a28SRiver Riddlecomponents, meaning that other dialects, such as our `Toy` dialect, must be
2305a8d5a28SRiver Riddleexplicitly loaded.
2315b4a01d4SMehdi Amini
2325b4a01d4SMehdi Amini## Defining Toy Operations
2335b4a01d4SMehdi Amini
2345a8d5a28SRiver RiddleNow that we have a `Toy` dialect, we can start defining the operations. This
2355a8d5a28SRiver Riddlewill allow for providing semantic information that the rest of the system can
2365a8d5a28SRiver Riddlehook into. As an example, let's walk through the creation of a `toy.constant`
2375a8d5a28SRiver Riddleoperation. This operation will represent a constant value in the Toy language.
2385b4a01d4SMehdi Amini
2395b4a01d4SMehdi Amini```mlir
2405b4a01d4SMehdi Amini %4 = "toy.constant"() {value = dense<1.0> : tensor<2x3xf64>} : () -> tensor<2x3xf64>
2415b4a01d4SMehdi Amini```
2425b4a01d4SMehdi Amini
2435b4a01d4SMehdi AminiThis operation takes zero operands, a
24431d1ae79SMarkus Böck[dense elements](../../Dialects/Builtin.md/#denseintorfpelementsattr) attribute named
2455a8d5a28SRiver Riddle`value` to represent the constant value, and returns a single result of
24631d1ae79SMarkus Böck[RankedTensorType](../../Dialects/Builtin.md/#rankedtensortype). An operation class
24731d1ae79SMarkus Böckinherits from the [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
248a90a09faSmlevesquedion`mlir::Op` class which also takes some optional [*traits*](../../Traits) to
2495a8d5a28SRiver Riddlecustomize its behavior. `Traits` are a mechanism with which we can inject
2505a8d5a28SRiver Riddleadditional behavior into an Operation, such as additional accessors,
2515a8d5a28SRiver Riddleverification, and more. Let's look below at a possible definition for the
2525a8d5a28SRiver Riddleconstant operation that we have described above:
2535b4a01d4SMehdi Amini
2545b4a01d4SMehdi Amini```c++
2555a8d5a28SRiver Riddleclass ConstantOp : public mlir::Op<
2565a8d5a28SRiver Riddle                     /// `mlir::Op` is a CRTP class, meaning that we provide the
2575a8d5a28SRiver Riddle                     /// derived class as a template parameter.
2585a8d5a28SRiver Riddle                     ConstantOp,
2595a8d5a28SRiver Riddle                     /// The ConstantOp takes zero input operands.
2605b4a01d4SMehdi Amini                     mlir::OpTrait::ZeroOperands,
2615b4a01d4SMehdi Amini                     /// The ConstantOp returns a single result.
2629eb3e564SChris Lattner                     mlir::OpTrait::OneResult,
2635a8d5a28SRiver Riddle                     /// We also provide a utility `getType` accessor that
2645a8d5a28SRiver Riddle                     /// returns the TensorType of the single result.
265*9d0fa42fSVishakh Prakash                     mlir::OpTrait::OneTypedResult<TensorType>::Impl> {
2665b4a01d4SMehdi Amini
2675b4a01d4SMehdi Amini public:
2685b4a01d4SMehdi Amini  /// Inherit the constructors from the base Op class.
2695b4a01d4SMehdi Amini  using Op::Op;
2705b4a01d4SMehdi Amini
2715b4a01d4SMehdi Amini  /// Provide the unique name for this operation. MLIR will use this to register
2725a8d5a28SRiver Riddle  /// the operation and uniquely identify it throughout the system. The name
2735a8d5a28SRiver Riddle  /// provided here must be prefixed by the parent dialect namespace followed
2745a8d5a28SRiver Riddle  /// by a `.`.
2755b4a01d4SMehdi Amini  static llvm::StringRef getOperationName() { return "toy.constant"; }
2765b4a01d4SMehdi Amini
2775b4a01d4SMehdi Amini  /// Return the value of the constant by fetching it from the attribute.
2785b4a01d4SMehdi Amini  mlir::DenseElementsAttr getValue();
2795b4a01d4SMehdi Amini
2805a8d5a28SRiver Riddle  /// Operations may provide additional verification beyond what the attached
2815a8d5a28SRiver Riddle  /// traits provide.  Here we will ensure that the specific invariants of the
2825a8d5a28SRiver Riddle  /// constant operation are upheld, for example the result type must be
2835a8d5a28SRiver Riddle  /// of TensorType and matches the type of the constant `value`.
2844e190c58SRiver Riddle  LogicalResult verifyInvariants();
2855b4a01d4SMehdi Amini
2865b4a01d4SMehdi Amini  /// Provide an interface to build this operation from a set of input values.
2875a8d5a28SRiver Riddle  /// This interface is used by the `builder` classes to allow for easily
2885a8d5a28SRiver Riddle  /// generating instances of this operation:
2895b4a01d4SMehdi Amini  ///   mlir::OpBuilder::create<ConstantOp>(...)
2905b4a01d4SMehdi Amini  /// This method populates the given `state` that MLIR uses to create
2915b4a01d4SMehdi Amini  /// operations. This state is a collection of all of the discrete elements
2925b4a01d4SMehdi Amini  /// that an operation may contain.
2935b4a01d4SMehdi Amini  /// Build a constant with the given return type and `value` attribute.
294898f74c3SAlex Zinenko  static void build(mlir::OpBuilder &builder, mlir::OperationState &state,
2955b4a01d4SMehdi Amini                    mlir::Type result, mlir::DenseElementsAttr value);
2965b4a01d4SMehdi Amini  /// Build a constant and reuse the type from the given 'value'.
297898f74c3SAlex Zinenko  static void build(mlir::OpBuilder &builder, mlir::OperationState &state,
2985b4a01d4SMehdi Amini                    mlir::DenseElementsAttr value);
2995b4a01d4SMehdi Amini  /// Build a constant by broadcasting the given 'value'.
300898f74c3SAlex Zinenko  static void build(mlir::OpBuilder &builder, mlir::OperationState &state,
3015b4a01d4SMehdi Amini                    double value);
3025b4a01d4SMehdi Amini};
3035b4a01d4SMehdi Amini```
3045b4a01d4SMehdi Amini
3055a8d5a28SRiver Riddleand we can register this operation in the `ToyDialect` initializer:
3065b4a01d4SMehdi Amini
3075b4a01d4SMehdi Amini```c++
308ee748605SRiver Riddlevoid ToyDialect::initialize() {
3095b4a01d4SMehdi Amini  addOperations<ConstantOp>();
3105b4a01d4SMehdi Amini}
3115b4a01d4SMehdi Amini```
3125b4a01d4SMehdi Amini
3135b4a01d4SMehdi Amini### Op vs Operation: Using MLIR Operations
3145b4a01d4SMehdi Amini
3155a8d5a28SRiver RiddleNow that we have defined an operation, we will want to access and transform it.
3165a8d5a28SRiver RiddleIn MLIR, there are two main classes related to operations: `Operation` and `Op`.
3175a8d5a28SRiver RiddleThe `Operation` class is used to generically model all operations. It is
3185a8d5a28SRiver Riddle'opaque', in the sense that it does not describe the properties of particular
3195a8d5a28SRiver Riddleoperations or types of operations. Instead, the `Operation` class provides a
3205a8d5a28SRiver Riddlegeneral API into an operation instance. On the other hand, each specific type of
3215a8d5a28SRiver Riddleoperation is represented by an `Op` derived class. For instance `ConstantOp`
3225a8d5a28SRiver Riddlerepresents a operation with zero inputs, and one output, which is always set to
3235a8d5a28SRiver Riddlethe same value. `Op` derived classes act as smart pointer wrapper around a
3245a8d5a28SRiver Riddle`Operation*`, provide operation-specific accessor methods, and type-safe
3255a8d5a28SRiver Riddleproperties of operations. This means that when we define our Toy operations, we
3265a8d5a28SRiver Riddleare simply defining a clean, semantically useful interface for building and
3275a8d5a28SRiver Riddleinterfacing with the `Operation` class. This is why our `ConstantOp` defines no
3285a8d5a28SRiver Riddleclass fields; all of the data for this operation is stored in the referenced
3295a8d5a28SRiver Riddle`Operation`. A side effect of this design is that we always pass around `Op`
3305a8d5a28SRiver Riddlederived classes "by-value", instead of by reference or pointer (*passing by
3315a8d5a28SRiver Riddlevalue* is a common idiom in MLIR and applies similarly to attributes, types,
3325a8d5a28SRiver Riddleetc). Given a generic `Operation*` instance, we can always get a specific `Op`
3335a8d5a28SRiver Riddleinstance using LLVM's casting infrastructure:
3345b4a01d4SMehdi Amini
3355b4a01d4SMehdi Amini```c++
3365b4a01d4SMehdi Aminivoid processConstantOp(mlir::Operation *operation) {
3375b4a01d4SMehdi Amini  ConstantOp op = llvm::dyn_cast<ConstantOp>(operation);
3385b4a01d4SMehdi Amini
3395b4a01d4SMehdi Amini  // This operation is not an instance of `ConstantOp`.
3405b4a01d4SMehdi Amini  if (!op)
3415b4a01d4SMehdi Amini    return;
3425b4a01d4SMehdi Amini
3439f6617dcSMatthias Kramm  // Get the internal operation instance wrapped by the smart pointer.
3445b4a01d4SMehdi Amini  mlir::Operation *internalOperation = op.getOperation();
3455b4a01d4SMehdi Amini  assert(internalOperation == operation &&
3465b4a01d4SMehdi Amini         "these operation instances are the same");
3475b4a01d4SMehdi Amini}
3485b4a01d4SMehdi Amini```
3495b4a01d4SMehdi Amini
3505b4a01d4SMehdi Amini### Using the Operation Definition Specification (ODS) Framework
3515b4a01d4SMehdi Amini
3525b4a01d4SMehdi AminiIn addition to specializing the `mlir::Op` C++ template, MLIR also supports
3535b4a01d4SMehdi Aminidefining operations in a declarative manner. This is achieved via the
3541294fa69SRiver Riddle[Operation Definition Specification](../../DefiningDialects/Operations.md) framework. Facts
3555b4a01d4SMehdi Aminiregarding an operation are specified concisely into a TableGen record, which
3565b4a01d4SMehdi Aminiwill be expanded into an equivalent `mlir::Op` C++ template specialization at
3575b4a01d4SMehdi Aminicompile time. Using the ODS framework is the desired way for defining operations
3585b4a01d4SMehdi Aminiin MLIR given the simplicity, conciseness, and general stability in the face of
3595b4a01d4SMehdi AminiC++ API changes.
3605b4a01d4SMehdi Amini
3615b4a01d4SMehdi AminiLets see how to define the ODS equivalent of our ConstantOp:
3625b4a01d4SMehdi Amini
363ee748605SRiver RiddleOperations in ODS are defined by inheriting from the `Op` class. To simplify our
364ee748605SRiver Riddleoperation definitions, we will define a base class for operations in the Toy
365ee748605SRiver Riddledialect.
3665b4a01d4SMehdi Amini
3675b4a01d4SMehdi Amini```tablegen
3685b4a01d4SMehdi Amini// Base class for toy dialect operations. This operation inherits from the base
3695b4a01d4SMehdi Amini// `Op` class in OpBase.td, and provides:
3705b4a01d4SMehdi Amini//   * The parent dialect of the operation.
3715b4a01d4SMehdi Amini//   * The mnemonic for the operation, or the name without the dialect prefix.
3725b4a01d4SMehdi Amini//   * A list of traits for the operation.
373697a5036SSanjoy Dasclass Toy_Op<string mnemonic, list<Trait> traits = []> :
3745b4a01d4SMehdi Amini    Op<Toy_Dialect, mnemonic, traits>;
3755b4a01d4SMehdi Amini```
3765b4a01d4SMehdi Amini
3775b4a01d4SMehdi AminiWith all of the preliminary pieces defined, we can begin to define the constant
3785b4a01d4SMehdi Aminioperation.
3795b4a01d4SMehdi Amini
3805b4a01d4SMehdi AminiWe define a toy operation by inheriting from our base 'Toy_Op' class above. Here
3815b4a01d4SMehdi Aminiwe provide the mnemonic and a list of traits for the operation. The
3821294fa69SRiver Riddle[mnemonic](../../DefiningDialects/Operations.md/#operation-name) here matches the one given in
3830ddba0bdSRiver Riddle`ConstantOp::getOperationName` without the dialect prefix; `toy.`. Missing here
3840ddba0bdSRiver Riddlefrom our C++ definition are the `ZeroOperands` and `OneResult` traits; these
3850ddba0bdSRiver Riddlewill be automatically inferred based upon the `arguments` and `results` fields
3860ddba0bdSRiver Riddlewe define later.
3875b4a01d4SMehdi Amini
3885b4a01d4SMehdi Amini```tablegen
3890ddba0bdSRiver Riddledef ConstantOp : Toy_Op<"constant"> {
3905b4a01d4SMehdi Amini}
3915b4a01d4SMehdi Amini```
3925b4a01d4SMehdi Amini
3935b4a01d4SMehdi AminiAt this point you probably might want to know what the C++ code generated by
3945b4a01d4SMehdi AminiTableGen looks like. Simply run the `mlir-tblgen` command with the
3955b4a01d4SMehdi Amini`gen-op-decls` or the `gen-op-defs` action like so:
3965b4a01d4SMehdi Amini
397430bba2aSJacques Pienaar```shell
3985b4a01d4SMehdi Amini${build_root}/bin/mlir-tblgen -gen-op-defs ${mlir_src_root}/examples/toy/Ch2/include/toy/Ops.td -I ${mlir_src_root}/include/
3995b4a01d4SMehdi Amini```
4005b4a01d4SMehdi Amini
4015b4a01d4SMehdi AminiDepending on the selected action, this will print either the `ConstantOp` class
4025b4a01d4SMehdi Aminideclaration or its implementation. Comparing this output to the hand-crafted
4035b4a01d4SMehdi Aminiimplementation is incredibly useful when getting started with TableGen.
4045b4a01d4SMehdi Amini
4055b4a01d4SMehdi Amini#### Defining Arguments and Results
4065b4a01d4SMehdi Amini
4075b4a01d4SMehdi AminiWith the shell of the operation defined, we can now provide the
4081294fa69SRiver Riddle[inputs](../../DefiningDialects/Operations.md/#operation-arguments) and
4091294fa69SRiver Riddle[outputs](../../DefiningDialects/Operations.md/#operation-results) to our operation. The
4105b4a01d4SMehdi Aminiinputs, or arguments, to an operation may be attributes or types for SSA operand
4115b4a01d4SMehdi Aminivalues. The results correspond to a set of types for the values produced by the
4125b4a01d4SMehdi Aminioperation:
4135b4a01d4SMehdi Amini
4145b4a01d4SMehdi Amini```tablegen
4150ddba0bdSRiver Riddledef ConstantOp : Toy_Op<"constant"> {
4165b4a01d4SMehdi Amini  // The constant operation takes an attribute as the only input.
4175b4a01d4SMehdi Amini  // `F64ElementsAttr` corresponds to a 64-bit floating-point ElementsAttr.
4185b4a01d4SMehdi Amini  let arguments = (ins F64ElementsAttr:$value);
4195b4a01d4SMehdi Amini
4205b4a01d4SMehdi Amini  // The constant operation returns a single value of TensorType.
4215b4a01d4SMehdi Amini  // F64Tensor corresponds to a 64-bit floating-point TensorType.
4225b4a01d4SMehdi Amini  let results = (outs F64Tensor);
4235b4a01d4SMehdi Amini}
4245b4a01d4SMehdi Amini```
4255b4a01d4SMehdi Amini
4265b4a01d4SMehdi AminiBy providing a name to the arguments or results, e.g. `$value`, ODS will
4275b4a01d4SMehdi Aminiautomatically generate a matching accessor: `DenseElementsAttr
4285b4a01d4SMehdi AminiConstantOp::value()`.
4295b4a01d4SMehdi Amini
4305b4a01d4SMehdi Amini#### Adding Documentation
4315b4a01d4SMehdi Amini
4325b4a01d4SMehdi AminiThe next step after defining the operation is to document it. Operations may
4335b4a01d4SMehdi Aminiprovide
4341294fa69SRiver Riddle[`summary` and `description`](../../DefiningDialects/Operations.md/#operation-documentation)
4355b4a01d4SMehdi Aminifields to describe the semantics of the operation. This information is useful
4365b4a01d4SMehdi Aminifor users of the dialect and can even be used to auto-generate Markdown
4375b4a01d4SMehdi Aminidocuments.
4385b4a01d4SMehdi Amini
4395b4a01d4SMehdi Amini```tablegen
4400ddba0bdSRiver Riddledef ConstantOp : Toy_Op<"constant"> {
4415b4a01d4SMehdi Amini  // Provide a summary and description for this operation. This can be used to
4425b4a01d4SMehdi Amini  // auto-generate documentation of the operations within our dialect.
4435b4a01d4SMehdi Amini  let summary = "constant operation";
4445b4a01d4SMehdi Amini  let description = [{
4455b4a01d4SMehdi Amini    Constant operation turns a literal into an SSA value. The data is attached
4465b4a01d4SMehdi Amini    to the operation as an attribute. For example:
4475b4a01d4SMehdi Amini
4485b4a01d4SMehdi Amini      %0 = "toy.constant"()
4495b4a01d4SMehdi Amini         { value = dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64> }
4505b4a01d4SMehdi Amini        : () -> tensor<2x3xf64>
4515b4a01d4SMehdi Amini  }];
4525b4a01d4SMehdi Amini
4535b4a01d4SMehdi Amini  // The constant operation takes an attribute as the only input.
4545b4a01d4SMehdi Amini  // `F64ElementsAttr` corresponds to a 64-bit floating-point ElementsAttr.
4555b4a01d4SMehdi Amini  let arguments = (ins F64ElementsAttr:$value);
4565b4a01d4SMehdi Amini
4575b4a01d4SMehdi Amini  // The generic call operation returns a single value of TensorType.
4585b4a01d4SMehdi Amini  // F64Tensor corresponds to a 64-bit floating-point TensorType.
4595b4a01d4SMehdi Amini  let results = (outs F64Tensor);
4605b4a01d4SMehdi Amini}
4615b4a01d4SMehdi Amini```
4625b4a01d4SMehdi Amini
4635b4a01d4SMehdi Amini#### Verifying Operation Semantics
4645b4a01d4SMehdi Amini
4655b4a01d4SMehdi AminiAt this point we've already covered a majority of the original C++ operation
4665b4a01d4SMehdi Aminidefinition. The next piece to define is the verifier. Luckily, much like the
4675b4a01d4SMehdi Amininamed accessor, the ODS framework will automatically generate a lot of the
4685b4a01d4SMehdi Amininecessary verification logic based upon the constraints we have given. This
4695b4a01d4SMehdi Aminimeans that we don't need to verify the structure of the return type, or even the
4705b4a01d4SMehdi Aminiinput attribute `value`. In many cases, additional verification is not even
4715b4a01d4SMehdi Amininecessary for ODS operations. To add additional verification logic, an operation
4721294fa69SRiver Riddlecan override the [`verifier`](../../DefiningDialects/Operations.md/#custom-verifier-code)
4735b4a01d4SMehdi Aminifield. The `verifier` field allows for defining a C++ code blob that will be run
4745b4a01d4SMehdi Aminias part of `ConstantOp::verify`. This blob can assume that all of the other
4755b4a01d4SMehdi Aminiinvariants of the operation have already been verified:
4765b4a01d4SMehdi Amini
4775b4a01d4SMehdi Amini```tablegen
4780ddba0bdSRiver Riddledef ConstantOp : Toy_Op<"constant"> {
4795b4a01d4SMehdi Amini  // Provide a summary and description for this operation. This can be used to
4805b4a01d4SMehdi Amini  // auto-generate documentation of the operations within our dialect.
4815b4a01d4SMehdi Amini  let summary = "constant operation";
4825b4a01d4SMehdi Amini  let description = [{
4835b4a01d4SMehdi Amini    Constant operation turns a literal into an SSA value. The data is attached
4845b4a01d4SMehdi Amini    to the operation as an attribute. For example:
4855b4a01d4SMehdi Amini
4865b4a01d4SMehdi Amini      %0 = "toy.constant"()
4875b4a01d4SMehdi Amini         { value = dense<[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]> : tensor<2x3xf64> }
4885b4a01d4SMehdi Amini        : () -> tensor<2x3xf64>
4895b4a01d4SMehdi Amini  }];
4905b4a01d4SMehdi Amini
4915b4a01d4SMehdi Amini  // The constant operation takes an attribute as the only input.
4925b4a01d4SMehdi Amini  // `F64ElementsAttr` corresponds to a 64-bit floating-point ElementsAttr.
4935b4a01d4SMehdi Amini  let arguments = (ins F64ElementsAttr:$value);
4945b4a01d4SMehdi Amini
4955b4a01d4SMehdi Amini  // The generic call operation returns a single value of TensorType.
4965b4a01d4SMehdi Amini  // F64Tensor corresponds to a 64-bit floating-point TensorType.
4975b4a01d4SMehdi Amini  let results = (outs F64Tensor);
4985b4a01d4SMehdi Amini
4994e190c58SRiver Riddle  // Add additional verification logic to the constant operation. Setting this bit
500db791b27SRamkumar Ramachandra  // to `1` will generate a `::llvm::LogicalResult verify()` declaration on the
5014e190c58SRiver Riddle  // operation class that is called after ODS constructs have been verified, for
5024e190c58SRiver Riddle  // example the types of arguments and results. We implement additional verification
5034e190c58SRiver Riddle  // in the definition of this `verify` method in the C++ source file.
5044e190c58SRiver Riddle  let hasVerifier = 1;
5055b4a01d4SMehdi Amini}
5065b4a01d4SMehdi Amini```
5075b4a01d4SMehdi Amini
5085b4a01d4SMehdi Amini#### Attaching `build` Methods
5095b4a01d4SMehdi Amini
5105b4a01d4SMehdi AminiThe final missing component here from our original C++ example are the `build`
5115b4a01d4SMehdi Aminimethods. ODS can generate some simple build methods automatically, and in this
5125b4a01d4SMehdi Aminicase it will generate our first build method for us. For the rest, we define the
5131294fa69SRiver Riddle[`builders`](../../DefiningDialects/Operations.md/#custom-builder-methods) field. This field
5145b4a01d4SMehdi Aminitakes a list of `OpBuilder` objects that take a string corresponding to a list
5155b4a01d4SMehdi Aminiof C++ parameters, as well as an optional code block that can be used to specify
5165b4a01d4SMehdi Aminithe implementation inline.
5175b4a01d4SMehdi Amini
5185b4a01d4SMehdi Amini```tablegen
5190ddba0bdSRiver Riddledef ConstantOp : Toy_Op<"constant"> {
5209f6617dcSMatthias Kramm  ...
5215b4a01d4SMehdi Amini
5225b4a01d4SMehdi Amini  // Add custom build methods for the constant operation. These methods populate
5235b4a01d4SMehdi Amini  // the `state` that MLIR uses to create operations, i.e. these are used when
5245b4a01d4SMehdi Amini  // using `builder.create<ConstantOp>(...)`.
5255b4a01d4SMehdi Amini  let builders = [
5265b4a01d4SMehdi Amini    // Build a constant with a given constant tensor value.
52732c49c7dSAlex Zinenko    OpBuilder<(ins "DenseElementsAttr":$value), [{
5285b4a01d4SMehdi Amini      // Call into an autogenerated `build` method.
5295b4a01d4SMehdi Amini      build(builder, result, value.getType(), value);
5305b4a01d4SMehdi Amini    }]>,
5315b4a01d4SMehdi Amini
5325b4a01d4SMehdi Amini    // Build a constant with a given constant floating-point value. This builder
5335b4a01d4SMehdi Amini    // creates a declaration for `ConstantOp::build` with the given parameters.
53432c49c7dSAlex Zinenko    OpBuilder<(ins "double":$value)>
5355b4a01d4SMehdi Amini  ];
5365b4a01d4SMehdi Amini}
5375b4a01d4SMehdi Amini```
5385b4a01d4SMehdi Amini
5390050e8f0SRiver Riddle#### Specifying a Custom Assembly Format
5405b4a01d4SMehdi Amini
541495cf272SLucy FoxAt this point we can generate our "Toy IR". For example, the following:
5425b4a01d4SMehdi Amini
543430bba2aSJacques Pienaar```toy
5445b4a01d4SMehdi Amini# User defined generic function that operates on unknown shaped arguments.
5455b4a01d4SMehdi Aminidef multiply_transpose(a, b) {
5465b4a01d4SMehdi Amini  return transpose(a) * transpose(b);
5475b4a01d4SMehdi Amini}
5485b4a01d4SMehdi Amini
5495b4a01d4SMehdi Aminidef main() {
5505b4a01d4SMehdi Amini  var a<2, 3> = [[1, 2, 3], [4, 5, 6]];
5515b4a01d4SMehdi Amini  var b<2, 3> = [1, 2, 3, 4, 5, 6];
5525b4a01d4SMehdi Amini  var c = multiply_transpose(a, b);
5535b4a01d4SMehdi Amini  var d = multiply_transpose(b, a);
5545b4a01d4SMehdi Amini  print(d);
5555b4a01d4SMehdi Amini}
5565b4a01d4SMehdi Amini```
5575b4a01d4SMehdi Amini
5585b4a01d4SMehdi AminiResults in the following IR:
5595b4a01d4SMehdi Amini
5605b4a01d4SMehdi Amini```mlir
5615b4a01d4SMehdi Aminimodule {
562ee2c6cd9SRiver Riddle  "toy.func"() ({
563ee2c6cd9SRiver Riddle  ^bb0(%arg0: tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":4:1), %arg1: tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":4:1)):
564495cf272SLucy Fox    %0 = "toy.transpose"(%arg0) : (tensor<*xf64>) -> tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":5:10)
565495cf272SLucy Fox    %1 = "toy.transpose"(%arg1) : (tensor<*xf64>) -> tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":5:25)
566495cf272SLucy Fox    %2 = "toy.mul"(%0, %1) : (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":5:25)
567495cf272SLucy Fox    "toy.return"(%2) : (tensor<*xf64>) -> () loc("test/Examples/Toy/Ch2/codegen.toy":5:3)
568ee2c6cd9SRiver Riddle  }) {sym_name = "multiply_transpose", type = (tensor<*xf64>, tensor<*xf64>) -> tensor<*xf64>} : () -> () loc("test/Examples/Toy/Ch2/codegen.toy":4:1)
569ee2c6cd9SRiver Riddle  "toy.func"() ({
570495cf272SLucy Fox    %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> loc("test/Examples/Toy/Ch2/codegen.toy":9:17)
571495cf272SLucy Fox    %1 = "toy.reshape"(%0) : (tensor<2x3xf64>) -> tensor<2x3xf64> loc("test/Examples/Toy/Ch2/codegen.toy":9:3)
572495cf272SLucy Fox    %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> loc("test/Examples/Toy/Ch2/codegen.toy":10:17)
573495cf272SLucy Fox    %3 = "toy.reshape"(%2) : (tensor<6xf64>) -> tensor<2x3xf64> loc("test/Examples/Toy/Ch2/codegen.toy":10:3)
574495cf272SLucy Fox    %4 = "toy.generic_call"(%1, %3) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":11:11)
575495cf272SLucy Fox    %5 = "toy.generic_call"(%3, %1) {callee = @multiply_transpose} : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":12:11)
576495cf272SLucy Fox    "toy.print"(%5) : (tensor<*xf64>) -> () loc("test/Examples/Toy/Ch2/codegen.toy":13:3)
577495cf272SLucy Fox    "toy.return"() : () -> () loc("test/Examples/Toy/Ch2/codegen.toy":8:1)
578ee2c6cd9SRiver Riddle  }) {sym_name = "main", type = () -> ()} : () -> () loc("test/Examples/Toy/Ch2/codegen.toy":8:1)
579495cf272SLucy Fox} loc(unknown)
5805b4a01d4SMehdi Amini```
5815b4a01d4SMehdi Amini
5820050e8f0SRiver RiddleOne thing to notice here is that all of our Toy operations are printed using the
5830050e8f0SRiver Riddlegeneric assembly format. This format is the one shown when breaking down
5840050e8f0SRiver Riddle`toy.transpose` at the beginning of this chapter. MLIR allows for operations to
5850050e8f0SRiver Riddledefine their own custom assembly format, either
5861294fa69SRiver Riddle[declaratively](../../DefiningDialects/Operations.md/#declarative-assembly-format) or
5870050e8f0SRiver Riddleimperatively via C++. Defining a custom assembly format allows for tailoring the
5880050e8f0SRiver Riddlegenerated IR into something a bit more readable by removing a lot of the fluff
5890050e8f0SRiver Riddlethat is required by the generic format. Let's walk through an example of an
5900050e8f0SRiver Riddleoperation format that we would like to simplify.
5910050e8f0SRiver Riddle
5920050e8f0SRiver Riddle##### `toy.print`
5930050e8f0SRiver Riddle
5940050e8f0SRiver RiddleThe current form of `toy.print` is a little verbose. There are a lot of
5950050e8f0SRiver Riddleadditional characters that we would like to strip away. Let's begin by thinking
5960050e8f0SRiver Riddleof what a good format of `toy.print` would be, and see how we can implement it.
5970050e8f0SRiver RiddleLooking at the basics of `toy.print` we get:
5980050e8f0SRiver Riddle
5990050e8f0SRiver Riddle```mlir
6000050e8f0SRiver Riddletoy.print %5 : tensor<*xf64> loc(...)
6010050e8f0SRiver Riddle```
6020050e8f0SRiver Riddle
6030050e8f0SRiver RiddleHere we have stripped much of the format down to the bare essentials, and it has
6040050e8f0SRiver Riddlebecome much more readable. To provide a custom assembly format, an operation can
60512bfd159SRiver Riddleeither override the `hasCustomAssemblyFormat` field for a C++ format, or the
6060050e8f0SRiver Riddle`assemblyFormat` field for the declarative format. Let's look at the C++ variant
6070050e8f0SRiver Riddlefirst, as this is what the declarative format maps to internally.
6080050e8f0SRiver Riddle
6090050e8f0SRiver Riddle```tablegen
6100050e8f0SRiver Riddle/// Consider a stripped definition of `toy.print` here.
6110050e8f0SRiver Riddledef PrintOp : Toy_Op<"print"> {
6120050e8f0SRiver Riddle  let arguments = (ins F64Tensor:$input);
6130050e8f0SRiver Riddle
61412bfd159SRiver Riddle  // Divert the printer and parser to `parse` and `print` methods on our operation,
61512bfd159SRiver Riddle  // to be implemented in the .cpp file. More details on these methods is shown below.
61612bfd159SRiver Riddle  let hasCustomAssemblyFormat = 1;
6170050e8f0SRiver Riddle}
6180050e8f0SRiver Riddle```
6190050e8f0SRiver Riddle
6200050e8f0SRiver RiddleA C++ implementation for the printer and parser is shown below:
6210050e8f0SRiver Riddle
6220050e8f0SRiver Riddle```c++
6230050e8f0SRiver Riddle/// The 'OpAsmPrinter' class is a stream that will allows for formatting
6240050e8f0SRiver Riddle/// strings, attributes, operands, types, etc.
62512bfd159SRiver Riddlevoid PrintOp::print(mlir::OpAsmPrinter &printer) {
6260050e8f0SRiver Riddle  printer << "toy.print " << op.input();
6270050e8f0SRiver Riddle  printer.printOptionalAttrDict(op.getAttrs());
6280050e8f0SRiver Riddle  printer << " : " << op.input().getType();
6290050e8f0SRiver Riddle}
6300050e8f0SRiver Riddle
6319f6617dcSMatthias Kramm/// The 'OpAsmParser' class provides a collection of methods for parsing
6320050e8f0SRiver Riddle/// various punctuation, as well as attributes, operands, types, etc. Each of
6330050e8f0SRiver Riddle/// these methods returns a `ParseResult`. This class is a wrapper around
6340050e8f0SRiver Riddle/// `LogicalResult` that can be converted to a boolean `true` value on failure,
6350050e8f0SRiver Riddle/// or `false` on success. This allows for easily chaining together a set of
6360050e8f0SRiver Riddle/// parser rules. These rules are used to populate an `mlir::OperationState`
6370050e8f0SRiver Riddle/// similarly to the `build` methods described above.
63812bfd159SRiver Riddlemlir::ParseResult PrintOp::parse(mlir::OpAsmParser &parser,
6390050e8f0SRiver Riddle                                 mlir::OperationState &result) {
6400050e8f0SRiver Riddle  // Parse the input operand, the attribute dictionary, and the type of the
6410050e8f0SRiver Riddle  // input.
642e13d23bcSMarkus Böck  mlir::OpAsmParser::UnresolvedOperand inputOperand;
6430050e8f0SRiver Riddle  mlir::Type inputType;
6440050e8f0SRiver Riddle  if (parser.parseOperand(inputOperand) ||
6450050e8f0SRiver Riddle      parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
6460050e8f0SRiver Riddle      parser.parseType(inputType))
6470050e8f0SRiver Riddle    return mlir::failure();
6480050e8f0SRiver Riddle
6490050e8f0SRiver Riddle  // Resolve the input operand to the type we parsed in.
6500050e8f0SRiver Riddle  if (parser.resolveOperand(inputOperand, inputType, result.operands))
6510050e8f0SRiver Riddle    return mlir::failure();
6520050e8f0SRiver Riddle
6530050e8f0SRiver Riddle  return mlir::success();
6540050e8f0SRiver Riddle}
6550050e8f0SRiver Riddle```
6560050e8f0SRiver Riddle
6570050e8f0SRiver RiddleWith the C++ implementation defined, let's see how this can be mapped to the
6581294fa69SRiver Riddle[declarative format](../../DefiningDialects/Operations.md/#declarative-assembly-format). The
6590050e8f0SRiver Riddledeclarative format is largely composed of three different components:
6600050e8f0SRiver Riddle
6610050e8f0SRiver Riddle*   Directives
6620050e8f0SRiver Riddle    -   A type of builtin function, with an optional set of arguments.
6630050e8f0SRiver Riddle*   Literals
6640050e8f0SRiver Riddle    -   A keyword or punctuation surrounded by \`\`.
6650050e8f0SRiver Riddle*   Variables
6660050e8f0SRiver Riddle    -   An entity that has been registered on the operation itself, i.e. an
6670050e8f0SRiver Riddle        argument(attribute or operand), result, successor, etc. In the `PrintOp`
6680050e8f0SRiver Riddle        example above, a variable would be `$input`.
6690050e8f0SRiver Riddle
6700050e8f0SRiver RiddleA direct mapping of our C++ format looks something like:
6710050e8f0SRiver Riddle
6720050e8f0SRiver Riddle```tablegen
6730050e8f0SRiver Riddle/// Consider a stripped definition of `toy.print` here.
6740050e8f0SRiver Riddledef PrintOp : Toy_Op<"print"> {
6750050e8f0SRiver Riddle  let arguments = (ins F64Tensor:$input);
6760050e8f0SRiver Riddle
6770050e8f0SRiver Riddle  // In the following format we have two directives, `attr-dict` and `type`.
6780050e8f0SRiver Riddle  // These correspond to the attribute dictionary and the type of a given
6790050e8f0SRiver Riddle  // variable represectively.
6800050e8f0SRiver Riddle  let assemblyFormat = "$input attr-dict `:` type($input)";
6810050e8f0SRiver Riddle}
6820050e8f0SRiver Riddle```
6830050e8f0SRiver Riddle
6841294fa69SRiver RiddleThe [declarative format](../../DefiningDialects/Operations.md/#declarative-assembly-format) has
6850050e8f0SRiver Riddlemany more interesting features, so be sure to check it out before implementing a
6860050e8f0SRiver Riddlecustom format in C++. After beautifying the format of a few of our operations we
6870050e8f0SRiver Riddlenow get a much more readable:
6880050e8f0SRiver Riddle
6890050e8f0SRiver Riddle```mlir
6900050e8f0SRiver Riddlemodule {
691ee2c6cd9SRiver Riddle  toy.func @multiply_transpose(%arg0: tensor<*xf64>, %arg1: tensor<*xf64>) -> tensor<*xf64> {
692495cf272SLucy Fox    %0 = toy.transpose(%arg0 : tensor<*xf64>) to tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":5:10)
693495cf272SLucy Fox    %1 = toy.transpose(%arg1 : tensor<*xf64>) to tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":5:25)
694495cf272SLucy Fox    %2 = toy.mul %0, %1 : tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":5:25)
695495cf272SLucy Fox    toy.return %2 : tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":5:3)
696495cf272SLucy Fox  } loc("test/Examples/Toy/Ch2/codegen.toy":4:1)
697ee2c6cd9SRiver Riddle  toy.func @main() {
698495cf272SLucy Fox    %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/Examples/Toy/Ch2/codegen.toy":9:17)
699495cf272SLucy Fox    %1 = toy.reshape(%0 : tensor<2x3xf64>) to tensor<2x3xf64> loc("test/Examples/Toy/Ch2/codegen.toy":9:3)
700495cf272SLucy Fox    %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/Examples/Toy/Ch2/codegen.toy":10:17)
701495cf272SLucy Fox    %3 = toy.reshape(%2 : tensor<6xf64>) to tensor<2x3xf64> loc("test/Examples/Toy/Ch2/codegen.toy":10:3)
702495cf272SLucy Fox    %4 = toy.generic_call @multiply_transpose(%1, %3) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":11:11)
703495cf272SLucy Fox    %5 = toy.generic_call @multiply_transpose(%3, %1) : (tensor<2x3xf64>, tensor<2x3xf64>) -> tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":12:11)
704495cf272SLucy Fox    toy.print %5 : tensor<*xf64> loc("test/Examples/Toy/Ch2/codegen.toy":13:3)
705495cf272SLucy Fox    toy.return loc("test/Examples/Toy/Ch2/codegen.toy":8:1)
706495cf272SLucy Fox  } loc("test/Examples/Toy/Ch2/codegen.toy":8:1)
707495cf272SLucy Fox} loc(unknown)
7080050e8f0SRiver Riddle```
7090050e8f0SRiver Riddle
7100050e8f0SRiver RiddleAbove we introduce several of the concepts for defining operations in the ODS
7110050e8f0SRiver Riddleframework, but there are many more that we haven't had a chance to: regions,
7120050e8f0SRiver Riddlevariadic operands, etc. Check out the
7131294fa69SRiver Riddle[full specification](../../DefiningDialects/Operations.md) for more details.
7140050e8f0SRiver Riddle
7150050e8f0SRiver Riddle## Complete Toy Example
7160050e8f0SRiver Riddle
717495cf272SLucy FoxWe can now generate our "Toy IR". You can build `toyc-ch2` and try yourself on
718495cf272SLucy Foxthe above example: `toyc-ch2 test/Examples/Toy/Ch2/codegen.toy -emit=mlir
719495cf272SLucy Fox-mlir-print-debuginfo`. We can also check our RoundTrip: `toyc-ch2
720495cf272SLucy Foxtest/Examples/Toy/Ch2/codegen.toy -emit=mlir -mlir-print-debuginfo 2>
721495cf272SLucy Foxcodegen.mlir` followed by `toyc-ch2 codegen.mlir -emit=mlir`. You should also
722495cf272SLucy Foxuse `mlir-tblgen` on the final definition file and study the generated C++ code.
7235b4a01d4SMehdi Amini
7245b4a01d4SMehdi AminiAt this point, MLIR knows about our Toy dialect and operations. In the
7255b4a01d4SMehdi Amini[next chapter](Ch-3.md), we will leverage our new dialect to implement some
7265b4a01d4SMehdi Aminihigh-level language-specific analyses and transformations for the Toy language.
727