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