3 **Current status**: Under development and not enabled by default
11 * A relatively recent Python3 installation
12 * Installation of python dependencies as specified in
13 `mlir/python/requirements.txt`
17 * **`MLIR_ENABLE_BINDINGS_PYTHON`**`:BOOL`
19 Enables building the Python bindings. Defaults to `OFF`.
21 * **`Python3_EXECUTABLE`**:`STRING`
23 Specifies the `python` executable used for the LLVM build, including for
24 determining header/link flags for the Python bindings. On systems with
25 multiple Python implementations, setting this explicitly to the preferred
26 `python3` executable is strongly recommended.
28 ### Recommended development practices
30 It is recommended to use a python virtual environment. Many ways exist for this,
31 but the following is the simplest:
34 # Make sure your 'python' is what you expect. Note that on multi-python
35 # systems, this may have a version suffix, and on many Linuxes and MacOS where
36 # python2 and python3 co-exist, you may also want to use `python3`.
38 python -m venv ~/.venv/mlirdev
39 source ~/.venv/mlirdev/bin/activate
41 # Note that many LTS distros will bundle a version of pip itself that is too
42 # old to download all of the latest binaries for certain platforms.
43 # The pip version can be obtained with `python -m pip --version`, and for
44 # Linux specifically, this should be cross checked with minimum versions
45 # here: https://github.com/pypa/manylinux
46 # It is recommended to upgrade pip:
47 python -m pip install --upgrade pip
50 # Now the `python` command will resolve to your virtual environment and
51 # packages will be installed there.
52 python -m pip install -r mlir/python/requirements.txt
54 # Now run `cmake`, `ninja`, et al.
57 For interactive use, it is sufficient to add the
58 `tools/mlir/python_packages/mlir_core/` directory in your `build/` directory to
59 the `PYTHONPATH`. Typically:
62 export PYTHONPATH=$(cd build && pwd)/tools/mlir/python_packages/mlir_core
65 Note that if you have installed (i.e. via `ninja install`, et al), then python
66 packages for all enabled projects will be in your install tree under
67 `python_packages/` (i.e. `python_packages/mlir_core`). Official distributions
68 are built with a more specialized setup.
74 There are likely two primary use cases for the MLIR python bindings:
76 1. Support users who expect that an installed version of LLVM/MLIR will yield
77 the ability to `import mlir` and use the API in a pure way out of the box.
79 1. Downstream integrations will likely want to include parts of the API in
80 their private namespace or specially built libraries, probably mixing it
81 with other python native bits.
83 ### Composable modules
85 In order to support use case \#2, the Python bindings are organized into
86 composable modules that downstream integrators can include and re-export into
87 their own namespace if desired. This forces several design points:
89 * Separate the construction/populating of a `py::module` from
90 `PYBIND11_MODULE` global constructor.
92 * Introduce headers for C++-only wrapper classes as other related C++ modules
93 will need to interop with it.
95 * Separate any initialization routines that depend on optional components into
96 its own module/dependency (currently, things like `registerAllDialects` fall
99 There are a lot of co-related issues of shared library linkage, distribution
100 concerns, etc that affect such things. Organizing the code into composable
101 modules (versus a monolithic `cpp` file) allows the flexibility to address many
102 of these as needed over time. Also, compilation time for all of the template
103 meta-programming in pybind scales with the number of things you define in a
104 translation unit. Breaking into multiple translation units can significantly aid
105 compile times for APIs with a large surface area.
109 Generally, the C++ codebase namespaces most things into the `mlir` namespace.
110 However, in order to modularize and make the Python bindings easier to
111 understand, sub-packages are defined that map roughly to the directory structure
112 of functional units in MLIR.
117 * `mlir.passes` (`pass` is a reserved word :( )
119 * `mlir.execution_engine` (aside from namespacing, it is important that
120 "bulky"/optional parts like this are isolated)
122 In addition, initialization functions that imply optional dependencies should be
123 in underscored (notionally private) modules such as `_init` and linked
124 separately. This allows downstream integrators to completely customize what is
125 included "in the box" and covers things like dialect registration, pass
130 LLVM/MLIR is a non-trivial python-native project that is likely to co-exist with
131 other non-trivial native extensions. As such, the native extension (i.e. the
132 `.so`/`.pyd`/`.dylib`) is exported as a notionally private top-level symbol
133 (`_mlir`), while a small set of Python code is provided in
134 `mlir/_cext_loader.py` and siblings which loads and re-exports it. This split
135 provides a place to stage code that needs to prepare the environment *before*
136 the shared library is loaded into the Python runtime, and also provides a place
137 that one-time initialization code can be invoked apart from module constructors.
139 It is recommended to avoid using `__init__.py` files to the extent possible,
140 until reaching a leaf package that represents a discrete component. The rule to
141 keep in mind is that the presence of an `__init__.py` file prevents the ability
142 to split anything at that level or below in the namespace into different
143 directories, deployment packages, wheels, etc.
145 See the documentation for more information and advice:
146 https://packaging.python.org/guides/packaging-namespace-packages/
150 The Python APIs should seek to layer on top of the C-API to the degree possible.
151 Especially for the core, dialect-independent parts, such a binding enables
152 packaging decisions that would be difficult or impossible if spanning a C++ ABI
153 boundary. In addition, factoring in this way side-steps some very difficult
154 issues that arise when combining RTTI-based modules (which pybind derived things
155 are) with non-RTTI polymorphic C++ code (the default compilation mode of LLVM).
157 ### Ownership in the Core IR
159 There are several top-level types in the core IR that are strongly owned by
160 their python-side reference:
162 * `PyContext` (`mlir.ir.Context`)
163 * `PyModule` (`mlir.ir.Module`)
164 * `PyOperation` (`mlir.ir.Operation`) - but with caveats
166 All other objects are dependent. All objects maintain a back-reference
167 (keep-alive) to their closest containing top-level object. Further, dependent
168 objects fall into two categories: a) uniqued (which live for the life-time of
169 the context) and b) mutable. Mutable objects need additional machinery for
170 keeping track of when the C++ instance that backs their Python object is no
171 longer valid (typically due to some specific mutation of the IR, deletion, or
174 ### Optionality and argument ordering in the Core IR
176 The following types support being bound to the current thread as a context
179 * `PyLocation` (`loc: mlir.ir.Location = None`)
180 * `PyInsertionPoint` (`ip: mlir.ir.InsertionPoint = None`)
181 * `PyMlirContext` (`context: mlir.ir.Context = None`)
183 In order to support composability of function arguments, when these types appear
184 as arguments, they should always be the last and appear in the above order and
185 with the given names (which is generally the order in which they are expected to
186 need to be expressed explicitly in special cases) as necessary. Each should
187 carry a default value of `py::none()` and use either a manual or automatic
188 conversion for resolving either with the explicit value or a value from the
189 thread context manager (i.e. `DefaultingPyMlirContext` or
190 `DefaultingPyLocation`).
192 The rationale for this is that in Python, trailing keyword arguments to the
193 *right* are the most composable, enabling a variety of strategies such as kwarg
194 passthrough, default values, etc. Keeping function signatures composable
195 increases the chances that interesting DSLs and higher level APIs can be
196 constructed without a lot of exotic boilerplate.
198 Used consistently, this enables a style of IR construction that rarely needs to
199 use explicit contexts, locations, or insertion points but is free to do so when
200 extra control is needed.
202 #### Operation hierarchy
204 As mentioned above, `PyOperation` is special because it can exist in either a
205 top-level or dependent state. The life-cycle is unidirectional: operations can
206 be created detached (top-level) and once added to another operation, they are
207 then dependent for the remainder of their lifetime. The situation is more
208 complicated when considering construction scenarios where an operation is added
209 to a transitive parent that is still detached, necessitating further accounting
210 at such transition points (i.e. all such added children are initially added to
211 the IR with a parent of their outer-most detached operation, but then once it is
212 added to an attached operation, they need to be re-parented to the containing
215 Due to the validity and parenting accounting needs, `PyOperation` is the owner
216 for regions and blocks and needs to be a top-level type that we can count on not
217 aliasing. This let's us do things like selectively invalidating instances when
218 mutations occur without worrying that there is some alias to the same operation
219 in the hierarchy. Operations are also the only entity that are allowed to be in
220 a detached state, and they are interned at the context level so that there is
221 never more than one Python `mlir.ir.Operation` object for a unique
222 `MlirOperation`, regardless of how it is obtained.
224 The C/C++ API allows for Region/Block to also be detached, but it simplifies the
225 ownership model a lot to eliminate that possibility in this API, allowing the
226 Region/Block to be completely dependent on its owning operation for accounting.
227 The aliasing of Python `Region`/`Block` instances to underlying
228 `MlirRegion`/`MlirBlock` is considered benign and these objects are not interned
229 in the context (unlike operations).
231 If we ever want to re-introduce detached regions/blocks, we could do so with new
232 "DetachedRegion" class or similar and also avoid the complexity of accounting.
233 With the way it is now, we can avoid having a global live list for regions and
234 blocks. We may end up needing an op-local one at some point TBD, depending on
235 how hard it is to guarantee how mutations interact with their Python peer
236 objects. We can cross that bridge easily when we get there.
238 Module, when used purely from the Python API, can't alias anyway, so we can use
239 it as a top-level ref type without a live-list for interning. If the API ever
240 changes such that this cannot be guaranteed (i.e. by letting you marshal a
241 native-defined Module in), then there would need to be a live table for it too.
245 ### Context Management
247 The bindings rely on Python
248 [context managers](https://docs.python.org/3/reference/datamodel.html#context-managers)
249 (`with` statements) to simplify creation and handling of IR objects by omitting
250 repeated arguments such as MLIR contexts, operation insertion points and
251 locations. A context manager sets up the default object to be used by all
252 binding calls within the following context and in the same thread. This default
253 can be overridden by specific calls through the dedicated keyword arguments.
257 An MLIR context is a top-level entity that owns attributes and types and is
258 referenced from virtually all IR constructs. Contexts also provide thread safety
259 at the C++ level. In Python bindings, the MLIR context is also a Python context
260 manager, one can write:
263 from mlir.ir import Context, Module
265 with Context() as ctx:
266 # IR construction using `ctx` as context.
268 # For example, parsing an MLIR module from string requires the context.
269 Module.parse("builtin.module {}")
272 IR objects referencing a context usually provide access to it through the
273 `.context` property. Most IR-constructing functions expect the context to be
274 provided in some form. In case of attributes and types, the context may be
275 extracted from the contained attribute or type. In case of operations, the
276 context is systematically extracted from Locations (see below). When the context
277 cannot be extracted from any argument, the bindings API expects the (keyword)
278 argument `context`. If it is not provided or set to `None` (default), it will be
279 looked up from an implicit stack of contexts maintained by the bindings in the
280 current thread and updated by context managers. If there is no surrounding
281 context, an error will be raised.
283 Note that it is possible to manually specify the MLIR context both inside and
284 outside of the `with` statement:
287 from mlir.ir import Context, Module
289 standalone_ctx = Context()
290 with Context() as managed_ctx:
291 # Parse a module in managed_ctx.
294 # Parse a module in standalone_ctx (override the context manager).
295 Module.parse("...", context=standalone_ctx)
297 # Parse a module without using context managers.
298 Module.parse("...", context=standalone_ctx)
301 The context object remains live as long as there are IR objects referencing it.
303 #### Insertion Points and Locations
305 When constructing an MLIR operation, two pieces of information are required:
307 - an *insertion point* that indicates where the operation is to be created in
308 the IR region/block/operation structure (usually before or after another
309 operation, or at the end of some block); it may be missing, at which point
310 the operation is created in the *detached* state;
311 - a *location* that contains user-understandable information about the source
312 of the operation (for example, file/line/column information), which must
313 always be provided as it carries a reference to the MLIR context.
315 Both can be provided using context managers or explicitly as keyword arguments
316 in the operation constructor. They can be also provided as keyword arguments
317 `ip` and `loc` both within and outside of the context manager.
320 from mlir.ir import Context, InsertionPoint, Location, Module, Operation
322 with Context() as ctx:
323 module = Module.create()
325 # Prepare for inserting operations into the body of the module and indicate
326 # that these operations originate in the "f.mlir" file at the given line and
328 with InsertionPoint(module.body), Location.file("f.mlir", line=42, col=1):
329 # This operation will be inserted at the end of the module body and will
330 # have the location set up by the context manager.
333 # This operation will be inserted at the end of the module (and after the
334 # previously constructed operation) and will have the location provided as
335 # the keyword argument.
336 Operation(<...>, loc=Location.file("g.mlir", line=1, col=10))
338 # This operation will be inserted at the *beginning* of the block rather
340 Operation(<...>, ip=InsertionPoint.at_block_begin(module.body))
343 Note that `Location` needs an MLIR context to be constructed. It can take the
344 context set up in the current thread by some surrounding context manager, or
345 accept it as an explicit argument:
348 from mlir.ir import Context, Location
350 # Create a context and a location in this context in the same `with` statement.
351 with Context() as ctx, Location.file("f.mlir", line=42, col=1, context=ctx):
355 Locations are owned by the context and live as long as they are (transitively)
356 referenced from somewhere in Python code.
358 Unlike locations, the insertion point may be left unspecified (or, equivalently,
359 set to `None` or `False`) during operation construction. In this case, the
360 operation is created in the *detached* state, that is, it is not added into the
361 region of another operation and is owned by the caller. This is usually the case
362 for top-level operations that contain the IR, such as modules. Regions, blocks
363 and values contained in an operation point back to it and maintain it live.
365 ### Inspecting IR Objects
367 Inspecting the IR is one of the primary tasks the Python bindings are designed
368 for. One can traverse the IR operation/region/block structure and inspect their
369 aspects such as operation attributes and value types.
371 #### Operations, Regions and Blocks
373 Operations are represented as either:
375 - the generic `Operation` class, useful in particular for generic processing
376 of unregistered operations; or
377 - a specific subclass of `OpView` that provides more semantically-loaded
378 accessors to operation properties.
380 Given an `OpView` subclass, one can obtain an `Operation` using its `.operation`
381 property. Given an `Operation`, one can obtain the corresponding `OpView` using
382 its `.opview` property *as long as* the corresponding class has been set up.
383 This typically means that the Python module of its dialect has been loaded. By
384 default, the `OpView` version is produced when navigating the IR tree.
386 One can check if an operation has a specific type by means of Python's
387 `isinstance` function:
392 if isinstance(operation.opview, mydialect.MyOp):
394 if isinstance(opview, mydialect.MyOp):
398 The components of an operation can be inspected using its properties.
400 - `attributes` is a collection of operation attributes . It can be subscripted
401 as both dictionary and sequence, e.g., both `operation.attributes["value"]`
402 and `operation.attributes[0]` will work. There is no guarantee on the order
403 in which the attributes are traversed when iterating over the `attributes`
404 property as sequence.
405 - `operands` is a sequence collection of operation operands.
406 - `results` is a sequence collection of operation results.
407 - `regions` is a sequence collection of regions attached to the operation.
409 The objects produced by `operands` and `results` have a `.types` property that
410 contains a sequence collection of types of the corresponding values.
413 from mlir.ir import Operation
417 if operation1.results.types == operation2.operand.types:
421 `OpView` subclasses for specific operations may provide leaner accessors to
422 properties of an operation. For example, named attributes, operand and results
423 are usually accessible as properties of the `OpView` subclass with the same
424 name, such as `operation.const_value` instead of
425 `operation.attributes["const_value"]`. If this name is a reserved Python
426 keyword, it is suffixed with an underscore.
428 The operation itself is iterable, which provides access to the attached regions
432 from mlir.ir import Operation
435 for region in operation:
436 do_something_with_region(region)
439 A region is conceptually a sequence of blocks. Objects of the `Region` class are
440 thus iterable, which provides access to the blocks. One can also use the
444 # Regions are directly iterable and give access to blocks.
445 for block1, block2 in zip(operation.regions[0], operation.regions[0].blocks)
446 assert block1 == block2
449 A block contains a sequence of operations, and has several additional
450 properties. Objects of the `Block` class are iterable and provide access to the
451 operations contained in the block. So does the `.operations` property. Blocks
452 also have a list of arguments available as a sequence collection using the
453 `.arguments` property.
455 Block and region belong to the parent operation in Python bindings and keep it
456 alive. This operation can be accessed using the `.owner` property.
458 #### Attributes and Types
460 Attributes and types are (mostly) immutable context-owned objects. They are
461 represented as either:
463 - an opaque `Attribute` or `Type` object supporting printing and comparison;
465 - a concrete subclass thereof with access to properties of the attribute or
468 Given an `Attribute` or `Type` object, one can obtain a concrete subclass using
469 the constructor of the subclass. This may raise a `ValueError` if the attribute
470 or type is not of the expected subclass:
473 from mlir.ir import Attribute, Type
474 from mlir.<dialect> import ConcreteAttr, ConcreteType
479 concrete_attr = ConcreteAttr(attribute)
480 concrete_type = ConcreteType(type)
481 except ValueError as e:
482 # Handle incorrect subclass.
485 In addition, concrete attribute and type classes provide a static `isinstance`
486 method to check whether an object of the opaque `Attribute` or `Type` type can
490 from mlir.ir import Attribute, Type
491 from mlir.<dialect> import ConcreteAttr, ConcreteType
496 # No need to handle errors here.
497 if ConcreteAttr.isinstance(attribute):
498 concrete_attr = ConcreteAttr(attribute)
499 if ConcreteType.isinstance(type):
500 concrete_type = ConcreteType(type)
503 By default, and unlike operations, attributes and types are returned from IR
504 traversals using the opaque `Attribute` or `Type` that needs to be downcasted.
506 Concrete attribute and type classes usually expose their properties as Python
507 readonly properties. For example, the elemental type of a tensor type can be
508 accessed using the `.element_type` property.
512 MLIR has two kinds of values based on their defining object: block arguments and
513 operation results. Values are handled similarly to attributes and types. They
514 are represented as either:
516 - a generic `Value` object; or
517 - a concrete `BlockArgument` or `OpResult` object.
519 The former provides all the generic functionality such as comparison, type
520 access and printing. The latter provide access to the defining block or
521 operation and the position of the value within it. By default, the generic
522 `Value` objects are returned from IR traversals. Downcasting is implemented
523 through concrete subclass constructors, similarly to attribtues and types:
526 from mlir.ir import BlockArgument, OpResult, Value
530 # Set `concrete` to the specific value subclass.
532 concrete = BlockArgument(value)
534 # This must not raise another ValueError as values are either block arguments
536 concrete = OpResult(value)
541 MLIR interfaces are a mechanism to interact with the IR without needing to know
542 specific types of operations but only some of their aspects. Operation
543 interfaces are available as Python classes with the same name as their C++
544 counterparts. Objects of these classes can be constructed from either:
546 - an object of the `Operation` class or of any `OpView` subclass; in this
547 case, all interface methods are available;
548 - a subclass of `OpView` and a context; in this case, only the *static*
549 interface methods are available as there is no associated operation.
551 In both cases, construction of the interface raises a `ValueError` if the
552 operation class does not implement the interface in the given context (or, for
553 operations, in the context that the operation is defined in). Similarly to
554 attributes and types, the MLIR context may be set up by a surrounding context
558 from mlir.ir import Context, InferTypeOpInterface
563 # Attempt to cast the operation into an interface.
565 iface = InferTypeOpInterface(op)
567 print("Operation does not implement InferTypeOpInterface.")
570 # All methods are available on interface objects constructed from an Operation
572 iface.someInstanceMethod()
574 # An interface object can also be constructed given an OpView subclass. It
575 # also needs a context in which the interface will be looked up. The context
576 # can be provided explicitly or set up by the surrounding context manager.
578 iface = InferTypeOpInterface(some_dialect.SomeOp)
580 print("SomeOp does not implement InferTypeOpInterface.")
583 # Calling an instance method on an interface object constructed from a class
584 # will raise TypeError.
586 iface.someInstanceMethod()
590 # One can still call static interface methods though.
591 iface.inferOpReturnTypes(<...>)
594 If an interface object was constructed from an `Operation` or an `OpView`, they
595 are available as `.operation` and `.opview` properties of the interface object,
598 Only a subset of operation interfaces are currently provided in Python bindings.
599 Attribute and type interfaces are not yet available in Python bindings.
601 ### Creating IR Objects
603 Python bindings also support IR creation and manipulation.
605 #### Operations, Regions and Blocks
607 Operations can be created given a `Location` and an optional `InsertionPoint`.
608 It is often easier to user context managers to specify locations and insertion
609 points for several operations created in a row as described above.
611 Concrete operations can be created by using constructors of the corresponding
612 `OpView` subclasses. The generic, default form of the constructor accepts:
614 - an optional sequence of types for operation results (`results`);
615 - an optional sequence of values for operation operands, or another operation
616 producing those values (`operands`);
617 - an optional dictionary of operation attributes (`attributes`);
618 - an optional sequence of successor blocks (`successors`);
619 - the number of regions to attach to the operation (`regions`, default `0`);
620 - the `loc` keyword argument containing the `Location` of this operation; if
621 `None`, the location created by the closest context manager is used or an
622 exception will be raised if there is no context manager;
623 - the `ip` keyword argument indicating where the operation will be inserted in
624 the IR; if `None`, the insertion point created by the closest context
625 manager is used; if there is no surrounding context manager, the operation
626 is created in the detached state.
628 Most operations will customize the constructor to accept a reduced list of
629 arguments that are relevant for the operation. For example, zero-result
630 operations may omit the `results` argument, so can the operations where the
631 result types can be derived from operand types unambiguously. As a concrete
632 example, built-in function operations can be constructed by providing a function
633 name as string and its argument and result types as a tuple of sequences:
636 from mlir.ir import Context, Module
637 from mlir.dialects import builtin
640 module = Module.create()
641 with InsertionPoint(module.body), Location.unknown():
642 func = func.FuncOp("main", ([], []))
645 Also see below for constructors generated from ODS.
647 Operations can also be constructed using the generic class and based on the
648 canonical string name of the operation using `Operation.create`. It accepts the
649 operation name as string, which must exactly match the canonical name of the
650 operation in C++ or ODS, followed by the same argument list as the default
651 constructor for `OpView`. *This form is discouraged* from use and is intended
652 for generic operation processing.
655 from mlir.ir import Context, Module
656 from mlir.dialects import builtin
659 module = Module.create()
660 with InsertionPoint(module.body), Location.unknown():
661 # Operations can be created in a generic way.
662 func = Operation.create(
663 "func.func", results=[], operands=[],
664 attributes={"function_type":TypeAttr.get(FunctionType.get([], []))},
665 successors=None, regions=1)
666 # The result will be downcasted to the concrete `OpView` subclass if
668 assert isinstance(func, func.FuncOp)
671 Regions are created for an operation when constructing it on the C++ side. They
672 are not constructible in Python and are not expected to exist outside of
673 operations (unlike in C++ that supports detached regions).
675 Blocks can be created within a given region and inserted before or after another
676 block of the same region using `create_before()`, `create_after()` methods of
677 the `Block` class, or the `create_at_start()` static method of the same class.
678 They are not expected to exist outside of regions (unlike in C++ that supports
682 from mlir.ir import Block, Context, Operation
685 op = Operation.create("generic.op", regions=1)
687 # Create the first block in the region.
688 entry_block = Block.create_at_start(op.regions[0])
690 # Create further blocks.
691 other_block = entry_block.create_after()
694 Blocks can be used to create `InsertionPoint`s, which can point to the beginning
695 or the end of the block, or just before its terminator. It is common for
696 `OpView` subclasses to provide a `.body` property that can be used to construct
697 an `InsertionPoint`. For example, builtin `Module` and `FuncOp` provide a
698 `.body` and `.add_entry_blocK()`, respectively.
700 #### Attributes and Types
702 Attributes and types can be created given a `Context` or another attribute or
703 type object that already references the context. To indicate that they are owned
704 by the context, they are obtained by calling the static `get` method on the
705 concrete attribute or type class. These method take as arguments the data
706 necessary to construct the attribute or type and a the keyword `context`
707 argument when the context cannot be derived from other arguments.
710 from mlir.ir import Context, F32Type, FloatAttr
712 # Attribute and types require access to an MLIR context, either directly or
713 # through another context-owned object.
715 f32 = F32Type.get(context=ctx)
716 pi = FloatAttr.get(f32, 3.14)
718 # They may use the context defined by the surrounding context manager.
721 pi = FloatAttr.get(f32, 3.14)
724 Some attributes provide additional construction methods for clarity.
727 from mlir.ir import Context, IntegerAttr, IntegerType
730 i8 = IntegerType.get_signless(8)
731 IntegerAttr.get(i8, 42)
734 Builtin attribute can often be constructed from Python types with similar
735 structure. For example, `ArrayAttr` can be constructed from a sequence
736 collection of attributes, and a `DictAttr` can be constructed from a dictionary:
739 from mlir.ir import ArrayAttr, Context, DictAttr, UnitAttr
742 array = ArrayAttr.get([UnitAttr.get(), UnitAttr.get()])
743 dictionary = DictAttr.get({"array": array, "unit": UnitAttr.get()})
746 Custom builders for Attributes to be used during Operation creation can be
747 registered by way of the `register_attribute_builder`. In particular the
748 following is how a custom builder is registered for `I32Attr`:
751 @register_attribute_builder("I32Attr")
752 def _i32Attr(x: int, context: Context):
753 return IntegerAttr.get(
754 IntegerType.get_signless(32, context=context), x)
757 This allows to invoke op creation of an op with a `I32Attr` with
763 The registration is based on the ODS name but registry is via pure python
764 method. Only single custom builder is allowed to be registered per ODS attribute
765 type (e.g., I32Attr can have only one, which can correspond to multiple of the
766 underlying IntegerAttr type).
771 foo.Op(IntegerAttr.get(IndexType.get_signless(32, context=context), 30))
776 In general, for the core parts of MLIR, the Python bindings should be largely
777 isomorphic with the underlying C++ structures. However, concessions are made
778 either for practicality or to give the resulting library an appropriately
781 ### Properties vs get\*() methods
783 Generally favor converting trivial methods like `getContext()`, `getName()`,
784 `isEntryBlock()`, etc to read-only Python properties (i.e. `context`). It is
785 primarily a matter of calling `def_property_readonly` vs `def` in binding code,
786 and makes things feel much nicer to the Python side.
791 m.def_property_readonly("context", ...)
797 m.def("getContext", ...)
802 Things that have nice printed representations are really great :) If there is a
803 reasonable printed form, it can be a significant productivity boost to wire that
804 to the `__repr__` method (and verify it with a [doctest](#sample-doctest)).
806 ### CamelCase vs snake\_case
808 Name functions/methods/properties in `snake_case` and classes in `CamelCase`. As
809 a mechanical concession to Python style, this can go a long way to making the
810 API feel like it fits in with its peers in the Python landscape.
812 If in doubt, choose names that will flow properly with other
813 [PEP 8 style names](https://pep8.org/#descriptive-naming-styles).
815 ### Prefer pseudo-containers
817 Many core IR constructs provide methods directly on the instance to query count
818 and begin/end iterators. Prefer hoisting these to dedicated pseudo containers.
820 For example, a direct mapping of blocks within regions could be done this way:
830 However, this way is preferred:
835 for block in region.blocks:
839 print(len(region.blocks))
840 print(region.blocks[0])
841 print(region.blocks[-1])
844 Instead of leaking STL-derived identifiers (`front`, `back`, etc), translate
845 them to appropriate `__dunder__` methods and iterator wrappers in the bindings.
847 Note that this can be taken too far, so use good judgment. For example, block
848 arguments may appear container-like but have defined methods for lookup and
849 mutation that would be hard to model properly without making semantics
850 complicated. If running into these, just mirror the C/C++ API.
852 ### Provide one stop helpers for common things
854 One stop helpers that aggregate over multiple low level entities can be
855 incredibly helpful and are encouraged within reason. For example, making
856 `Context` have a `parse_asm` or equivalent that avoids needing to explicitly
857 construct a SourceMgr can be quite nice. One stop helpers do not have to be
858 mutually exclusive with a more complete mapping of the backing constructs.
862 Tests should be added in the `test/Bindings/Python` directory and should
863 typically be `.py` files that have a lit run line.
865 We use `lit` and `FileCheck` based tests:
867 * For generative tests (those that produce IR), define a Python module that
868 constructs/prints the IR and pipe it through `FileCheck`.
869 * Parsing should be kept self-contained within the module under test by use of
870 raw constants and an appropriate `parse_asm` call.
871 * Any file I/O code should be staged through a tempfile vs relying on file
872 artifacts/paths outside of the test module.
873 * For convenience, we also test non-generative API interactions with the same
874 mechanisms, printing and `CHECK`ing as needed.
876 ### Sample FileCheck test
879 # RUN: %PYTHON %s | mlir-opt -split-input-file | FileCheck
881 # TODO: Move to a test utility class once any of this actually exists.
885 print("// TEST_FUNCTION:", f.__name__)
889 # CHECK-LABEL: TEST_FUNCTION: create_my_op
893 builder = m.new_op_builder()
894 # CHECK: mydialect.my_operation ...
899 ## Integration with ODS
901 The MLIR Python bindings integrate with the tablegen-based ODS system for
902 providing user-friendly wrappers around MLIR dialects and operations. There are
903 multiple parts to this integration, outlined below. Most details have been
904 elided: refer to the build rules and python sources under `mlir.dialects` for
905 the canonical way to use this facility.
907 Users are responsible for providing a `{DIALECT_NAMESPACE}.py` (or an equivalent
908 directory with `__init__.py` file) as the entrypoint.
910 ### Generating `_{DIALECT_NAMESPACE}_ops_gen.py` wrapper modules
912 Each dialect with a mapping to python requires that an appropriate
913 `_{DIALECT_NAMESPACE}_ops_gen.py` wrapper module is created. This is done by
914 invoking `mlir-tblgen` on a python-bindings specific tablegen wrapper that
915 includes the boilerplate and actual dialect specific `td` file. An example, for
916 the `Func` (which is assigned the namespace `func` as a special case):
919 #ifndef PYTHON_BINDINGS_FUNC_OPS
920 #define PYTHON_BINDINGS_FUNC_OPS
922 include "mlir/Dialect/Func/IR/FuncOps.td"
924 #endif // PYTHON_BINDINGS_FUNC_OPS
927 In the main repository, building the wrapper is done via the CMake function
928 `declare_mlir_dialect_python_bindings`, which invokes:
931 mlir-tblgen -gen-python-op-bindings -bind-dialect={DIALECT_NAMESPACE} \
932 {PYTHON_BINDING_TD_FILE}
935 The generates op classes must be included in the `{DIALECT_NAMESPACE}.py` file
936 in a similar way that generated headers are included for C++ generated code:
939 from ._my_dialect_ops_gen import *
942 ### Extending the search path for wrapper modules
944 When the python bindings need to locate a wrapper module, they consult the
945 `dialect_search_path` and use it to find an appropriately named module. For the
946 main repository, this search path is hard-coded to include the `mlir.dialects`
947 module, which is where wrappers are emitted by the above build rule. Out of tree
948 dialects can add their modules to the search path by calling:
951 from mlir.dialects._ods_common import _cext
952 _cext.globals.append_dialect_search_prefix("myproject.mlir.dialects")
955 ### Wrapper module code organization
957 The wrapper module tablegen emitter outputs:
959 * A `_Dialect` class (extending `mlir.ir.Dialect`) with a `DIALECT_NAMESPACE`
961 * An `{OpName}` class for each operation (extending `mlir.ir.OpView`).
962 * Decorators for each of the above to register with the system.
964 Note: In order to avoid naming conflicts, all internal names used by the wrapper
965 module are prefixed by `_ods_`.
967 Each concrete `OpView` subclass further defines several public-intended
970 * `OPERATION_NAME` attribute with the `str` fully qualified operation name
972 * An `__init__` method for the *default builder* if one is defined or inferred
974 * `@property` getter for each operand or result (using an auto-generated name
975 for unnamed of each).
976 * `@property` getter, setter and deleter for each declared attribute.
978 It further emits additional private-intended attributes meant for subclassing
979 and customization (default cases omit these attributes in favor of the defaults
982 * `_ODS_REGIONS`: A specification on the number and types of regions.
983 Currently a tuple of (min_region_count, has_no_variadic_regions). Note that
984 the API does some light validation on this but the primary purpose is to
985 capture sufficient information to perform other default building and region
987 * `_ODS_OPERAND_SEGMENTS` and `_ODS_RESULT_SEGMENTS`: Black-box value which
988 indicates the structure of either the operand or results with respect to
989 variadics. Used by `OpView._ods_build_default` to decode operand and result
990 lists that contain lists.
994 Presently, only a single, default builder is mapped to the `__init__` method.
995 The intent is that this `__init__` method represents the *most specific* of the
996 builders typically generated for C++; however currently it is just the generic
999 * One argument for each declared result:
1000 * For single-valued results: Each will accept an `mlir.ir.Type`.
1001 * For variadic results: Each will accept a `List[mlir.ir.Type]`.
1002 * One argument for each declared operand or attribute:
1003 * For single-valued operands: Each will accept an `mlir.ir.Value`.
1004 * For variadic operands: Each will accept a `List[mlir.ir.Value]`.
1005 * For attributes, it will accept an `mlir.ir.Attribute`.
1006 * Trailing usage-specific, optional keyword arguments:
1007 * `loc`: An explicit `mlir.ir.Location` to use. Defaults to the location
1008 bound to the thread (i.e. `with Location.unknown():`) or an error if
1009 none is bound nor specified.
1010 * `ip`: An explicit `mlir.ir.InsertionPoint` to use. Default to the
1011 insertion point bound to the thread (i.e. `with InsertionPoint(...):`).
1013 In addition, each `OpView` inherits a `build_generic` method which allows
1014 construction via a (nested in the case of variadic) sequence of `results` and
1015 `operands`. This can be used to get some default construction semantics for
1016 operations that are otherwise unsupported in Python, at the expense of having a
1017 very generic signature.
1019 #### Extending Generated Op Classes
1021 As mentioned above, the build system generates Python sources like
1022 `_{DIALECT_NAMESPACE}_ops_gen.py` for each dialect with Python bindings. It is
1023 often desirable to use these generated classes as a starting point for
1024 further customization, so an extension mechanism is provided to make this easy.
1025 This mechanism uses conventional inheritance combined with `OpView` registration.
1026 For example, the default builder for `arith.constant`
1029 class ConstantOp(_ods_ir.OpView):
1030 OPERATION_NAME = "arith.constant"
1032 _ODS_REGIONS = (0, True)
1034 def __init__(self, value, *, loc=None, ip=None):
1038 expects `value` to be a `TypedAttr` (e.g., `IntegerAttr` or `FloatAttr`).
1039 Thus, a natural extension is a builder that accepts a MLIR type and a Python value and instantiates the appropriate `TypedAttr`:
1042 from typing import Union
1044 from mlir.ir import Type, IntegerAttr, FloatAttr
1045 from mlir.dialects._arith_ops_gen import _Dialect, ConstantOp
1046 from mlir.dialects._ods_common import _cext
1048 @_cext.register_operation(_Dialect, replace=True)
1049 class ConstantOpExt(ConstantOp):
1051 self, result: Type, value: Union[int, float], *, loc=None, ip=None
1053 if isinstance(value, int):
1054 super().__init__(IntegerAttr.get(result, value), loc=loc, ip=ip)
1055 elif isinstance(value, float):
1056 super().__init__(FloatAttr.get(result, value), loc=loc, ip=ip)
1058 raise NotImplementedError(f"Building `arith.constant` not supported for {result=} {value=}")
1061 which enables building an instance of `arith.constant` like so:
1064 from mlir.ir import F32Type
1066 a = ConstantOpExt(F32Type.get(), 42.42)
1067 b = ConstantOpExt(IntegerType.get_signless(32), 42)
1070 Note, three key aspects of the extension mechanism in this example:
1072 1. `ConstantOpExt` directly inherits from the generated `ConstantOp`;
1073 2. in this, simplest, case all that's required is a call to the super class' initializer, i.e., `super().__init__(...)`;
1074 3. in order to register `ConstantOpExt` as the preferred `OpView` that is returned by `mlir.ir.Operation.opview` (see [Operations, Regions and Blocks](#operations-regions-and-blocks))
1075 we decorate the class with `@_cext.register_operation(_Dialect, replace=True)`, **where the `replace=True` must be used**.
1077 In some more complex cases it might be necessary to explicitly build the `OpView` through `OpView.build_generic` (see [Default Builder](#default-builder)), just as is performed by the generated builders.
1078 I.e., we must call `OpView.build_generic` **and pass the result to `OpView.__init__`**, where the small issue becomes that the latter is already overridden by the generated builder.
1079 Thus, we must call a method of a super class' super class (the "grandparent"); for example:
1082 from mlir.dialects._scf_ops_gen import _Dialect, ForOp
1083 from mlir.dialects._ods_common import _cext
1085 @_cext.register_operation(_Dialect, replace=True)
1086 class ForOpExt(ForOp):
1087 def __init__(self, lower_bound, upper_bound, step, iter_args, *, loc=None, ip=None):
1089 super(ForOp, self).__init__(self.build_generic(...))
1092 where `OpView.__init__` is called via `super(ForOp, self).__init__`.
1093 Note, there are alternatives ways to implement this (e.g., explicitly writing `OpView.__init__`); see any discussion on Python inheritance.
1095 ## Providing Python bindings for a dialect
1097 Python bindings are designed to support MLIR’s open dialect ecosystem. A dialect
1098 can be exposed to Python as a submodule of `mlir.dialects` and interoperate with
1099 the rest of the bindings. For dialects containing only operations, it is
1100 sufficient to provide Python APIs for those operations. Note that the majority
1101 of boilerplate APIs can be generated from ODS. For dialects containing
1102 attributes and types, it is necessary to thread those through the C API since
1103 there is no generic mechanism to create attributes and types. Passes need to be
1104 registered with the context in order to be usable in a text-specified pass
1105 manager, which may be done at Python module load time. Other functionality can
1106 be provided, similar to attributes and types, by exposing the relevant C API and
1107 building Python API on top.
1112 Dialect operations are provided in Python by wrapping the generic
1113 `mlir.ir.Operation` class with operation-specific builder functions and
1114 properties. Therefore, there is no need to implement a separate C API for them.
1115 For operations defined in ODS, `mlir-tblgen -gen-python-op-bindings
1116 -bind-dialect=<dialect-namespace>` generates the Python API from the declarative
1118 It is sufficient to create a new `.td` file that includes the original ODS
1119 definition and use it as source for the `mlir-tblgen` call.
1120 Such `.td` files reside in
1121 [`python/mlir/dialects/`](https://github.com/llvm/llvm-project/tree/main/mlir/python/mlir/dialects).
1122 The results of `mlir-tblgen` are expected to produce a file named
1123 `_<dialect-namespace>_ops_gen.py` by convention. The generated operation classes
1124 can be extended as described above. MLIR provides [CMake
1125 functions](https://github.com/llvm/llvm-project/blob/main/mlir/cmake/modules/AddMLIRPython.cmake)
1126 to automate the production of such files. Finally, a
1127 `python/mlir/dialects/<dialect-namespace>.py` or a
1128 `python/mlir/dialects/<dialect-namespace>/__init__.py` file must be created and
1129 filled with `import`s from the generated files to enable `import
1130 mlir.dialects.<dialect-namespace>` in Python.
1133 ### Attributes and Types
1135 Dialect attributes and types are provided in Python as subclasses of the
1136 `mlir.ir.Attribute` and `mlir.ir.Type` classes, respectively. Python APIs for
1137 attributes and types must connect to the relevant C APIs for building and
1138 inspection, which must be provided first. Bindings for `Attribute` and `Type`
1139 subclasses can be defined using
1140 [`include/mlir/Bindings/Python/PybindAdaptors.h`](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Bindings/Python/PybindAdaptors.h)
1141 utilities that mimic pybind11 API for defining functions and properties. These
1142 bindings are to be included in a separate pybind11 module. The utilities also
1143 provide automatic casting between C API handles `MlirAttribute` and `MlirType`
1144 and their Python counterparts so that the C API handles can be used directly in
1145 binding implementations. The methods and properties provided by the bindings
1146 should follow the principles discussed above.
1148 The attribute and type bindings for a dialect can be located in
1149 `lib/Bindings/Python/Dialect<Name>.cpp` and should be compiled into a separate
1150 “Python extension” library placed in `python/mlir/_mlir_libs` that will be
1151 loaded by Python at runtime. MLIR provides [CMake
1152 functions](https://github.com/llvm/llvm-project/blob/main/mlir/cmake/modules/AddMLIRPython.cmake)
1153 to automate the production of such libraries. This library should be `import`ed
1154 from the main dialect file, i.e. `python/mlir/dialects/<dialect-namespace>.py`
1155 or `python/mlir/dialects/<dialect-namespace>/__init__.py`, to ensure the types
1156 are available when the dialect is loaded from Python.
1161 Dialect-specific passes can be made available to the pass manager in Python by
1162 registering them with the context and relying on the API for pass pipeline
1163 parsing from string descriptions. This can be achieved by creating a new
1164 pybind11 module, defined in `lib/Bindings/Python/<Dialect>Passes.cpp`, that
1165 calls the registration C API, which must be provided first. For passes defined
1166 declaratively using Tablegen, `mlir-tblgen -gen-pass-capi-header` and
1167 `-mlir-tblgen -gen-pass-capi-impl` automate the generation of C API. The
1168 pybind11 module must be compiled into a separate “Python extension” library,
1169 which can be `import`ed from the main dialect file, i.e.
1170 `python/mlir/dialects/<dialect-namespace>.py` or
1171 `python/mlir/dialects/<dialect-namespace>/__init__.py`, or from a separate
1172 `passes` submodule to be put in
1173 `python/mlir/dialects/<dialect-namespace>/passes.py` if it is undesirable to
1174 make the passes available along with the dialect.
1177 ### Other functionality
1179 Dialect functionality other than IR objects or passes, such as helper functions,
1180 can be exposed to Python similarly to attributes and types. C API is expected to
1181 exist for this functionality, which can then be wrapped using pybind11 and
1182 `[include/mlir/Bindings/Python/PybindAdaptors.h](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Bindings/Python/PybindAdaptors.h)`
1183 utilities to connect to the rest of Python API. The bindings can be located in a
1184 separate pybind11 module or in the same module as attributes and types, and
1185 loaded along with the dialect.