1# MLIR Python Bindings
2
3**Current status**: Under development and not enabled by default
4
5[TOC]
6
7## Building
8
9### Pre-requisites
10
11* A relatively recent Python3 installation
12* Installation of python dependencies as specified in
13  `mlir/python/requirements.txt`
14
15### CMake variables
16
17* **`MLIR_ENABLE_BINDINGS_PYTHON`**`:BOOL`
18
19  Enables building the Python bindings. Defaults to `OFF`.
20
21* **`Python3_EXECUTABLE`**:`STRING`
22
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.
27
28* **`MLIR_BINDINGS_PYTHON_LOCK_VERSION`**`:BOOL`
29
30  Links the native extension against the Python runtime library, which is
31  optional on some platforms. While setting this to `OFF` can yield some greater
32  deployment flexibility, linking in this way allows the linker to report
33  compile time errors for unresolved symbols on all platforms, which makes for a
34  smoother development workflow. Defaults to `ON`.
35
36### Recommended development practices
37
38It is recommended to use a python virtual environment. Many ways exist for this,
39but the following is the simplest:
40
41```shell
42# Make sure your 'python' is what you expect. Note that on multi-python
43# systems, this may have a version suffix, and on many Linuxes and MacOS where
44# python2 and python3 co-exist, you may also want to use `python3`.
45which python
46python -m venv ~/.venv/mlirdev
47source ~/.venv/mlirdev/bin/activate
48
49# Note that many LTS distros will bundle a version of pip itself that is too
50# old to download all of the latest binaries for certain platforms.
51# The pip version can be obtained with `python -m pip --version`, and for
52# Linux specifically, this should be cross checked with minimum versions
53# here: https://github.com/pypa/manylinux
54# It is recommended to upgrade pip:
55python -m pip install --upgrade pip
56
57
58# Now the `python` command will resolve to your virtual environment and
59# packages will be installed there.
60python -m pip install -r mlir/python/requirements.txt
61
62# Now run `cmake`, `ninja`, et al.
63```
64
65For interactive use, it is sufficient to add the
66`tools/mlir/python_packages/mlir_core/` directory in your `build/` directory to
67the `PYTHONPATH`. Typically:
68
69```shell
70export PYTHONPATH=$(cd build && pwd)/tools/mlir/python_packages/mlir_core
71```
72
73Note that if you have installed (i.e. via `ninja install`, et al), then
74python packages for all enabled projects will be in your install tree under
75`python_packages/` (i.e. `python_packages/mlir_core`). Official distributions
76are built with a more specialized setup.
77
78## Design
79
80### Use cases
81
82There are likely two primary use cases for the MLIR python bindings:
83
841. Support users who expect that an installed version of LLVM/MLIR will yield
85   the ability to `import mlir` and use the API in a pure way out of the box.
86
871. Downstream integrations will likely want to include parts of the API in their
88   private namespace or specially built libraries, probably mixing it with other
89   python native bits.
90
91### Composable modules
92
93In order to support use case \#2, the Python bindings are organized into
94composable modules that downstream integrators can include and re-export into
95their own namespace if desired. This forces several design points:
96
97* Separate the construction/populating of a `py::module` from `PYBIND11_MODULE`
98  global constructor.
99
100* Introduce headers for C++-only wrapper classes as other related C++ modules
101  will need to interop with it.
102
103* Separate any initialization routines that depend on optional components into
104  its own module/dependency (currently, things like `registerAllDialects` fall
105  into this category).
106
107There are a lot of co-related issues of shared library linkage, distribution
108concerns, etc that affect such things. Organizing the code into composable
109modules (versus a monolithic `cpp` file) allows the flexibility to address many
110of these as needed over time. Also, compilation time for all of the template
111meta-programming in pybind scales with the number of things you define in a
112translation unit. Breaking into multiple translation units can significantly aid
113compile times for APIs with a large surface area.
114
115### Submodules
116
117Generally, the C++ codebase namespaces most things into the `mlir` namespace.
118However, in order to modularize and make the Python bindings easier to
119understand, sub-packages are defined that map roughly to the directory structure
120of functional units in MLIR.
121
122Examples:
123
124* `mlir.ir`
125* `mlir.passes` (`pass` is a reserved word :( )
126* `mlir.dialect`
127* `mlir.execution_engine` (aside from namespacing, it is important that
128  "bulky"/optional parts like this are isolated)
129
130In addition, initialization functions that imply optional dependencies should
131be in underscored (notionally private) modules such as `_init` and linked
132separately. This allows downstream integrators to completely customize what is
133included "in the box" and covers things like dialect registration,
134pass registration, etc.
135
136### Loader
137
138LLVM/MLIR is a non-trivial python-native project that is likely to co-exist with
139other non-trivial native extensions. As such, the native extension (i.e. the
140`.so`/`.pyd`/`.dylib`) is exported as a notionally private top-level symbol
141(`_mlir`), while a small set of Python code is provided in
142`mlir/_cext_loader.py` and siblings which loads and re-exports it. This
143split provides a place to stage code that needs to prepare the environment
144*before* the shared library is loaded into the Python runtime, and also
145provides a place that one-time initialization code can be invoked apart from
146module constructors.
147
148It is recommended to avoid using `__init__.py` files to the extent possible,
149until reaching a leaf package that represents a discrete component. The rule
150to keep in mind is that the presence of an `__init__.py` file prevents the
151ability to split anything at that level or below in the namespace into
152different directories, deployment packages, wheels, etc.
153
154See the documentation for more information and advice:
155https://packaging.python.org/guides/packaging-namespace-packages/
156
157### Use the C-API
158
159The Python APIs should seek to layer on top of the C-API to the degree possible.
160Especially for the core, dialect-independent parts, such a binding enables
161packaging decisions that would be difficult or impossible if spanning a C++ ABI
162boundary. In addition, factoring in this way side-steps some very difficult
163issues that arise when combining RTTI-based modules (which pybind derived things
164are) with non-RTTI polymorphic C++ code (the default compilation mode of LLVM).
165
166### Ownership in the Core IR
167
168There are several top-level types in the core IR that are strongly owned by their python-side reference:
169
170* `PyContext` (`mlir.ir.Context`)
171* `PyModule` (`mlir.ir.Module`)
172* `PyOperation` (`mlir.ir.Operation`) - but with caveats
173
174All other objects are dependent. All objects maintain a back-reference
175(keep-alive) to their closest containing top-level object. Further, dependent
176objects fall into two categories: a) uniqued (which live for the life-time of
177the context) and b) mutable. Mutable objects need additional machinery for
178keeping track of when the C++ instance that backs their Python object is no
179longer valid (typically due to some specific mutation of the IR, deletion, or
180bulk operation).
181
182### Optionality and argument ordering in the Core IR
183
184The following types support being bound to the current thread as a context manager:
185
186* `PyLocation` (`loc: mlir.ir.Location = None`)
187* `PyInsertionPoint` (`ip: mlir.ir.InsertionPoint = None`)
188* `PyMlirContext` (`context: mlir.ir.Context = None`)
189
190In order to support composability of function arguments, when these types appear
191as arguments, they should always be the last and appear in the above order and
192with the given names (which is generally the order in which they are expected to
193need to be expressed explicitly in special cases) as necessary. Each should
194carry a default value of `py::none()` and use either a manual or automatic
195conversion for resolving either with the explicit value or a value from the
196thread context manager (i.e. `DefaultingPyMlirContext` or
197`DefaultingPyLocation`).
198
199The rationale for this is that in Python, trailing keyword arguments to the
200*right* are the most composable, enabling a variety of strategies such as kwarg
201passthrough, default values, etc. Keeping function signatures composable
202increases the chances that interesting DSLs and higher level APIs can be
203constructed without a lot of exotic boilerplate.
204
205Used consistently, this enables a style of IR construction that rarely needs to
206use explicit contexts, locations, or insertion points but is free to do so when
207extra control is needed.
208
209#### Operation hierarchy
210
211As mentioned above, `PyOperation` is special because it can exist in either a
212top-level or dependent state. The life-cycle is unidirectional: operations can
213be created detached (top-level) and once added to another operation, they are
214then dependent for the remainder of their lifetime. The situation is more
215complicated when considering construction scenarios where an operation is added
216to a transitive parent that is still detached, necessitating further accounting
217at such transition points (i.e. all such added children are initially added to
218the IR with a parent of their outer-most detached operation, but then once it is
219added to an attached operation, they need to be re-parented to the containing
220module).
221
222Due to the validity and parenting accounting needs, `PyOperation` is the owner
223for regions and blocks and needs to be a top-level type that we can count on not
224aliasing. This let's us do things like selectively invalidating instances when
225mutations occur without worrying that there is some alias to the same operation
226in the hierarchy. Operations are also the only entity that are allowed to be in
227a detached state, and they are interned at the context level so that there is
228never more than one Python `mlir.ir.Operation` object for a unique
229`MlirOperation`, regardless of how it is obtained.
230
231The C/C++ API allows for Region/Block to also be detached, but it simplifies the
232ownership model a lot to eliminate that possibility in this API, allowing the
233Region/Block to be completely dependent on its owning operation for accounting.
234The aliasing of Python `Region`/`Block` instances to underlying
235`MlirRegion`/`MlirBlock` is considered benign and these objects are not interned
236in the context (unlike operations).
237
238If we ever want to re-introduce detached regions/blocks, we could do so with new
239"DetachedRegion" class or similar and also avoid the complexity of accounting.
240With the way it is now, we can avoid having a global live list for regions and
241blocks. We may end up needing an op-local one at some point TBD, depending on
242how hard it is to guarantee how mutations interact with their Python peer
243objects. We can cross that bridge easily when we get there.
244
245Module, when used purely from the Python API, can't alias anyway, so we can use
246it as a top-level ref type without a live-list for interning. If the API ever
247changes such that this cannot be guaranteed (i.e. by letting you marshal a
248native-defined Module in), then there would need to be a live table for it too.
249
250## User-level API
251
252### Context Management
253
254The bindings rely on Python
255[context managers](https://docs.python.org/3/reference/datamodel.html#context-managers)
256(`with` statements) to simplify creation and handling of IR objects by omitting
257repeated arguments such as MLIR contexts, operation insertion points and
258locations. A context manager sets up the default object to be used by all
259binding calls within the following context and in the same thread. This default
260can be overridden by specific calls through the dedicated keyword arguments.
261
262#### MLIR Context
263
264An MLIR context is a top-level entity that owns attributes and types and is
265referenced from virtually all IR constructs. Contexts also provide thread safety
266at the C++ level. In Python bindings, the MLIR context is also a Python context
267manager, one can write:
268
269```python
270from mlir.ir import Context, Module
271
272with Context() as ctx:
273  # IR construction using `ctx` as context.
274
275  # For example, parsing an MLIR module from string requires the context.
276  Module.parse("builtin.module {}")
277```
278
279IR objects referencing a context usually provide access to it through the
280`.context` property. Most IR-constructing functions expect the context to be
281provided in some form. In case of attributes and types, the context may be
282extracted from the contained attribute or type. In case of operations, the
283context is systematically extracted from Locations (see below). When the context
284cannot be extracted from any argument, the bindings API expects the (keyword)
285argument `context`. If it is not provided or set to `None` (default), it will be
286looked up from an implicit stack of contexts maintained by the bindings in the
287current thread and updated by context managers. If there is no surrounding
288context, an error will be raised.
289
290Note that it is possible to manually specify the MLIR context both inside and
291outside of the `with` statement:
292
293```python
294from mlir.ir import Context, Module
295
296standalone_ctx = Context()
297with Context() as managed_ctx:
298  # Parse a module in managed_ctx.
299  Module.parse("...")
300
301  # Parse a module in standalone_ctx (override the context manager).
302  Module.parse("...", context=standalone_ctx)
303
304# Parse a module without using context managers.
305Module.parse("...", context=standalone_ctx)
306```
307
308The context object remains live as long as there are IR objects referencing it.
309
310#### Insertion Points and Locations
311
312When constructing an MLIR operation, two pieces of information are required:
313
314-   an *insertion point* that indicates where the operation is to be created in
315    the IR region/block/operation structure (usually before or after another
316    operation, or at the end of some block); it may be missing, at which point
317    the operation is created in the *detached* state;
318-   a *location* that contains user-understandable information about the source
319    of the operation (for example, file/line/column information), which must
320    always be provided as it carries a reference to the MLIR context.
321
322Both can be provided using context managers or explicitly as keyword arguments
323in the operation constructor. They can be also provided as keyword arguments
324`ip` and `loc` both within and outside of the context manager.
325
326```python
327from mlir.ir import Context, InsertionPoint, Location, Module, Operation
328
329with Context() as ctx:
330  module = Module.create()
331
332  # Prepare for inserting operations into the body of the module and indicate
333  # that these operations originate in the "f.mlir" file at the given line and
334  # column.
335  with InsertionPoint(module.body), Location.file("f.mlir", line=42, col=1):
336    # This operation will be inserted at the end of the module body and will
337    # have the location set up by the context manager.
338    Operation(<...>)
339
340    # This operation will be inserted at the end of the module (and after the
341    # previously constructed operation) and will have the location provided as
342    # the keyword argument.
343    Operation(<...>, loc=Location.file("g.mlir", line=1, col=10))
344
345    # This operation will be inserted at the *beginning* of the block rather
346    # than at its end.
347    Operation(<...>, ip=InsertionPoint.at_block_begin(module.body))
348```
349
350Note that `Location` needs an MLIR context to be constructed. It can take the
351context set up in the current thread by some surrounding context manager, or
352accept it as an explicit argument:
353
354```python
355from mlir.ir import Context, Location
356
357# Create a context and a location in this context in the same `with` statement.
358with Context() as ctx, Location.file("f.mlir", line=42, col=1, context=ctx):
359  pass
360```
361
362Locations are owned by the context and maintain it live as long as they are
363(transitively) referenced from somewhere in Python code.
364
365Unlike locations, the insertion point may be left unspecified (or, equivalently,
366set to `None` or `False`) during operation construction. In this case, the
367operation is created in the *detached* state, that is, it is not added into the
368region of another operation and is owned by the caller. This is usually the case
369for top-level operations that contain the IR, such as modules. Regions, blocks
370and values contained in an operation point back to it and maintain it live.
371
372### Inspecting IR Objects
373
374Inspecting the IR is one of the primary tasks the Python bindings are designed
375for. One can traverse the IR operation/region/block structure and inspect their
376aspects such as operation attributes and value types.
377
378#### Operations, Regions and Blocks
379
380Operations are represented as either:
381
382-   the generic `Operation` class, useful in particular for generic processing
383    of unregistered operations; or
384-   a specific subclass of `OpView` that provides more semantically-loaded
385    accessors to operation properties.
386
387Given an `OpView` subclass, one can obtain an `Operation` using its `.operation`
388property. Given an `Operation`, one can obtain the corresponding `OpView` using
389its `.opview` property *as long as* the corresponding class has been set up.
390This typically means that the Python module of its dialect has been loaded. By
391default, the `OpView` version is produced when navigating the IR tree.
392
393One can check if an operation has a specific type by means of Python's
394`isinstance` function:
395
396```python
397operation = <...>
398opview = <...>
399if isinstance(operation.opview, mydialect.MyOp):
400  pass
401if isinstance(opview, mydialect.MyOp):
402  pass
403```
404
405The components of an operation can be inspected using its properties.
406
407-   `attributes` is a collection of operation attributes . It can be subscripted
408    as both dictionary and sequence, e.g., both `operation.attributes["value"]`
409    and `operation.attributes[0]` will work. There is no guarantee on the order
410    in which the attributes are traversed when iterating over the `attributes`
411    property as sequence.
412-   `operands` is a sequence collection of operation operands.
413-   `results` is a sequence collection of operation results.
414-   `regions` is a sequence collection of regions attached to the operation.
415
416The objects produced by `operands` and `results` have a `.types` property that
417contains a sequence collection of types of the corresponding values.
418
419```python
420from mlir.ir import Operation
421
422operation1 = <...>
423operation2 = <...>
424if operation1.results.types == operation2.operand.types:
425  pass
426```
427
428`OpView` subclasses for specific operations may provide leaner accessors to
429properties of an opeation. For example, named attributes, operand and results
430are usually accessible as properties of the `OpView` subclass with the same
431name, such as `operation.const_value` instead of
432`operation.attributes["const_value"]`. If this name is a reserved Python
433keyword, it is suffixed with an underscore.
434
435The operation itself is iterable, which provides access to the attached regions
436in order:
437
438```python
439from mlir.ir import Operation
440
441operation = <...>
442for region in operation:
443  do_something_with_region(region)
444```
445
446A region is conceptually a sequence of blocks. Objects of the `Region` class are
447thus iterable, which provides access to the blocks. One can also use the
448`.blocks` property.
449
450```python
451# Regions are directly iterable and give acceess to blocks.
452for block1, block2 in zip(operation.regions[0], operation.regions[0].blocks)
453  assert block1 == block2
454```
455
456A block contains a sequence of operations, and has several additional
457properties. Objects of the `Block` class are iterable and provide access to the
458operations contained in the block. So does the `.operations` property. Blocks
459also have a list of arguments available as a sequence collection using the
460`.arguments` property.
461
462Block and region belong to the parent operation in Python bindings and keep it
463alive. This operation can be accessed using the `.owner` property.
464
465#### Attributes and Types
466
467Attributes and types are (mostly) immutable context-owned objects. They are
468represented as either:
469
470-   an opaque `Attribute` or `Type` object supporting printing and comparsion;
471    or
472-   a concrete subclass thereof with access to properties of the attribute or
473    type.
474
475Given an `Attribute` or `Type` object, one can obtain a concrete subclass using
476the constructor of the subclass. This may raise a `ValueError` if the attribute
477or type does not have the expected subclass:
478
479```python
480from mlir.ir import Attribute, Type
481from mlir.<dialect> import ConcreteAttr, ConcreteType
482
483attribute = <...>
484type = <...>
485try:
486  concrete_attr = ConcreteAttr(attribute)
487  concrete_type = ConcreteType(type)
488except ValueError as e:
489  # Handle incorrect subclass.
490```
491
492In addition, concrete attribute and type classes provide a static `isinstance`
493method to check whether an object of the opaque `Attribute` or `Type` type can
494be downcasted:
495
496```python
497from mlir.ir import Attribute, Type
498from mlir.<dialect> import ConcreteAttr, ConcreteType
499
500attribute = <...>
501type = <...>
502
503# No need to handle errors here.
504if ConcreteAttr.isinstance(attribute):
505  concrete_attr = ConcreteAttr(attribute)
506if ConcreteType.isinstance(type):
507  concrete_type = ConcreteType(type)
508```
509
510By default, and unlike operations, attributes and types are returned from IR
511traversals using the opaque `Attribute` or `Type` that needs to be downcasted.
512
513Concrete attribute and type classes usually expose their properties as Python
514readonly properties. For example, the elemental type of a tensor type can be
515accessed using the `.element_type` property.
516
517#### Values
518
519MLIR has two kinds of values based on their defining object: block arguments and
520operation results. Values are handled similarly to attributes and types. They
521are represented as either:
522
523-   a generic `Value` object; or
524-   a concrete `BlockArgument` or `OpResult` object.
525
526The former provides all the generic functionality such as comparison, type
527access and printing. The latter provide access to the defining block or
528operation and the position of the value within it. By default, the generic
529`Value` objects are returned from IR traversals. Downcasting is implemented
530through concrete subclass constructors, similarly to attribtues and types:
531
532```python
533from mlir.ir import BlockArgument, OpResult, Value
534
535value = ...
536
537# Set `concrete` to the specific value subclass.
538try:
539  concrete = BlockArgument(value)
540except ValueError:
541  # This must not raise another ValueError as values are either block arguments
542  # or op results.
543  concrete = OpResult(value)
544```
545
546### Creating IR Objects
547
548Python bindings also support IR creation and manipulation.
549
550#### Operations, Regions and Blocks
551
552Operations can be created given a `Location` and an optional `InsertionPoint`.
553It is often easier to user context managers to specify locations and insertion
554points for several operations created in a row as decribed above.
555
556Concrete operations can be created by using constructors of the corresponding
557`OpView` subclasses. The generic, default form of the constructor accepts:
558
559-   an optional sequence of types for operation results (`results`);
560-   an optional sequence of values for operation operands, or another operation
561    producing those values (`operands`);
562-   an optional dictionary of operation attributes (`attributes`);
563-   an optional sequence of successor blocks (`successors`);
564-   the number of regions to attach to the operation (`regions`, default `0`);
565-   the `loc` keyword argument containing the `Location` of this operation; if
566    `None`, the location created by the closest context manager is used or an
567    exception will be raised if there is no context manager;
568-   the `ip` keyword argument indicating where the operation will be inserted in
569    the IR; if `None`, the insertion point created by the closest context
570    manager is used; if there is no surrounding context manager, the operation
571    is created in the detached state.
572
573Most operations will customize the constructor to accept a reduced list of
574arguments that are relevant for the operation. For example, zero-result
575operations may omit the `results` argument, so can the operations where the
576result types can be derived from operand types unambiguously. As a concrete
577example, built-in function operations can be constructed by providing a function
578name as string and its argument and result types as a tuple of sequences:
579
580```python
581from mlir.ir import Context, Module
582from mlir.dialects import builtin
583
584with Context():
585  module = Module.create()
586  with InsertionPoint(module.body), Location.unknown():
587    func = builtin.FuncOp("main", ([], []))
588```
589
590Also see below for constructors generated from ODS.
591
592Operations can also be constructed using the generic class and based on the
593canonical string name of the operation using `Operation.create`. It accepts the
594operation name as string, which must exactly match the canonical name of the
595operation in C++ or ODS, followed by the same argument list as the default
596constructor for `OpView`. *This form is discouraged* from use and is intended
597for generic operation processing.
598
599```python
600from mlir.ir import Context, Module
601from mlir.dialects import builtin
602
603with Context():
604  module = Module.create()
605  with InsertionPoint(module.body), Location.unknown():
606    # Operations can be created in a generic way.
607    func = Operation.create(
608        "builtin.func", results=[], operands=[],
609        attributes={"type":TypeAttr.get(FunctionType.get([], []))},
610        successors=None, regions=1)
611    # The result will be downcasted to the concrete `OpView` subclass if
612    # available.
613    assert isinstance(func, builtin.FuncOp)
614```
615
616Regions are created for an operation when constructing it on the C++ side. They
617are not constructible in Python and are not expected to exist outside of
618operations (unlike in C++ that supports detached regions).
619
620Blocks can be created within a given region and inserted before or after another
621block of the same region using `create_before()`, `create_after()` methods of
622the `Block` class. They are not expected to exist outside of regions (unlike in
623C++ that supports detached blocks).
624
625Blocks can be used to create `InsertionPoint`s, which can point to the beginning
626or the end of the block, or just before its terminator. It is common for
627`OpView` subclasses to provide a `.body` property that can be used to construct
628an `InsertionPoint`. For example, builtin `Module` and `FuncOp` provide a
629`.body` and `.add_entry_blocK()`, respectively.
630
631#### Attributes and Types
632
633Attributes and types can be created given a `Context` or another attribute or
634type object that already references the context. To indicate that they are owned
635by the context, they are obtained by calling the static `get` method on the
636concrete attribute or type class. These method take as arguments the data
637necessary to construct the attribute or type and a the keyword `context`
638argument when the context cannot be derived from other arguments.
639
640```python
641from mlir.ir import Context, F32Type, FloatAttr
642
643# Attribute and types require access to an MLIR context, either directly or
644# through another context-owned object.
645ctx = Context()
646f32 = F32Type.get(context=ctx)
647pi = FloatAttr.get(f32, 3.14)
648
649# They may use the context defined by the surrounding context manager.
650with Context():
651  f32 = F32Type.get()
652  pi = FloatAttr.get(f32, 3.14)
653```
654
655Some attributes provide additional construction methods for clarity.
656
657```python
658from mlir.ir import Context, IntegerAttr, IntegerType
659
660with Context():
661  i8 = IntegerType.get_signless(8)
662  IntegerAttr.get(i8, 42)
663```
664
665Builtin attribute can often be constructed from Python types with similar
666structure. For example, `ArrayAttr` can be constructed from a sequence
667collection of attributes, and a `DictAttr` can be constructed from a dictionary:
668
669```python
670from mlir.ir import ArrayAttr, Context, DictAttr, UnitAttr
671
672with Context():
673  array = ArrayAttr.get([UnitAttr.get(), UnitAttr.get()])
674  dictionary = DictAttr.get({"array": array, "unit": UnitAttr.get()})
675```
676
677## Style
678
679In general, for the core parts of MLIR, the Python bindings should be largely
680isomorphic with the underlying C++ structures. However, concessions are made
681either for practicality or to give the resulting library an appropriately
682"Pythonic" flavor.
683
684### Properties vs get\*() methods
685
686Generally favor converting trivial methods like `getContext()`, `getName()`,
687`isEntryBlock()`, etc to read-only Python properties (i.e. `context`). It is
688primarily a matter of calling `def_property_readonly` vs `def` in binding code,
689and makes things feel much nicer to the Python side.
690
691For example, prefer:
692
693```c++
694m.def_property_readonly("context", ...)
695```
696
697Over:
698
699```c++
700m.def("getContext", ...)
701```
702
703### __repr__ methods
704
705Things that have nice printed representations are really great :)  If there is a
706reasonable printed form, it can be a significant productivity boost to wire that
707to the `__repr__` method (and verify it with a [doctest](#sample-doctest)).
708
709### CamelCase vs snake\_case
710
711Name functions/methods/properties in `snake_case` and classes in `CamelCase`. As
712a mechanical concession to Python style, this can go a long way to making the
713API feel like it fits in with its peers in the Python landscape.
714
715If in doubt, choose names that will flow properly with other
716[PEP 8 style names](https://pep8.org/#descriptive-naming-styles).
717
718### Prefer pseudo-containers
719
720Many core IR constructs provide methods directly on the instance to query count
721and begin/end iterators. Prefer hoisting these to dedicated pseudo containers.
722
723For example, a direct mapping of blocks within regions could be done this way:
724
725```python
726region = ...
727
728for block in region:
729
730  pass
731```
732
733However, this way is preferred:
734
735```python
736region = ...
737
738for block in region.blocks:
739
740  pass
741
742print(len(region.blocks))
743print(region.blocks[0])
744print(region.blocks[-1])
745```
746
747Instead of leaking STL-derived identifiers (`front`, `back`, etc), translate
748them to appropriate `__dunder__` methods and iterator wrappers in the bindings.
749
750Note that this can be taken too far, so use good judgment. For example, block
751arguments may appear container-like but have defined methods for lookup and
752mutation that would be hard to model properly without making semantics
753complicated. If running into these, just mirror the C/C++ API.
754
755### Provide one stop helpers for common things
756
757One stop helpers that aggregate over multiple low level entities can be
758incredibly helpful and are encouraged within reason. For example, making
759`Context` have a `parse_asm` or equivalent that avoids needing to explicitly
760construct a SourceMgr can be quite nice. One stop helpers do not have to be
761mutually exclusive with a more complete mapping of the backing constructs.
762
763## Testing
764
765Tests should be added in the `test/Bindings/Python` directory and should
766typically be `.py` files that have a lit run line.
767
768We use `lit` and `FileCheck` based tests:
769
770* For generative tests (those that produce IR), define a Python module that
771  constructs/prints the IR and pipe it through `FileCheck`.
772* Parsing should be kept self-contained within the module under test by use of
773  raw constants and an appropriate `parse_asm` call.
774* Any file I/O code should be staged through a tempfile vs relying on file
775  artifacts/paths outside of the test module.
776* For convenience, we also test non-generative API interactions with the same
777  mechanisms, printing and `CHECK`ing as needed.
778
779### Sample FileCheck test
780
781```python
782# RUN: %PYTHON %s | mlir-opt -split-input-file | FileCheck
783
784# TODO: Move to a test utility class once any of this actually exists.
785def print_module(f):
786  m = f()
787  print("// -----")
788  print("// TEST_FUNCTION:", f.__name__)
789  print(m.to_asm())
790  return f
791
792# CHECK-LABEL: TEST_FUNCTION: create_my_op
793@print_module
794def create_my_op():
795  m = mlir.ir.Module()
796  builder = m.new_op_builder()
797  # CHECK: mydialect.my_operation ...
798  builder.my_op()
799  return m
800```
801
802## Integration with ODS
803
804The MLIR Python bindings integrate with the tablegen-based ODS system for
805providing user-friendly wrappers around MLIR dialects and operations. There
806are multiple parts to this integration, outlined below. Most details have
807been elided: refer to the build rules and python sources under `mlir.dialects`
808for the canonical way to use this facility.
809
810Users are responsible for providing a `{DIALECT_NAMESPACE}.py` (or an
811equivalent directory with `__init__.py` file) as the entrypoint.
812
813### Generating `_{DIALECT_NAMESPACE}_ops_gen.py` wrapper modules
814
815Each dialect with a mapping to python requires that an appropriate
816`_{DIALECT_NAMESPACE}_ops_gen.py` wrapper module is created. This is done by
817invoking `mlir-tblgen` on a python-bindings specific tablegen wrapper that
818includes the boilerplate and actual dialect specific `td` file. An example, for
819the `StandardOps` (which is assigned the namespace `std` as a special case):
820
821```tablegen
822#ifndef PYTHON_BINDINGS_STANDARD_OPS
823#define PYTHON_BINDINGS_STANDARD_OPS
824
825include "mlir/Bindings/Python/Attributes.td"
826include "mlir/Dialect/StandardOps/IR/Ops.td"
827
828#endif
829```
830
831In the main repository, building the wrapper is done via the CMake function
832`add_mlir_dialect_python_bindings`, which invokes:
833
834```
835mlir-tblgen -gen-python-op-bindings -bind-dialect={DIALECT_NAMESPACE} \
836    {PYTHON_BINDING_TD_FILE}
837```
838
839The generates op classes must be included in the `{DIALECT_NAMESPACE}.py` file
840in a similar way that generated headers are included for C++ generated code:
841
842```python
843from ._my_dialect_ops_gen import *
844```
845
846### Extending the search path for wrapper modules
847
848When the python bindings need to locate a wrapper module, they consult the
849`dialect_search_path` and use it to find an appropriately named module. For
850the main repository, this search path is hard-coded to include the
851`mlir.dialects` module, which is where wrappers are emitted by the abobe build
852rule. Out of tree dialects and add their modules to the search path by calling:
853
854```python
855mlir._cext.append_dialect_search_prefix("myproject.mlir.dialects")
856```
857
858### Wrapper module code organization
859
860The wrapper module tablegen emitter outputs:
861
862* A `_Dialect` class (extending `mlir.ir.Dialect`) with a `DIALECT_NAMESPACE`
863  attribute.
864* An `{OpName}` class for each operation (extending `mlir.ir.OpView`).
865* Decorators for each of the above to register with the system.
866
867Note: In order to avoid naming conflicts, all internal names used by the wrapper
868module are prefixed by `_ods_`.
869
870Each concrete `OpView` subclass further defines several public-intended
871attributes:
872
873* `OPERATION_NAME` attribute with the `str` fully qualified operation name
874  (i.e. `std.absf`).
875* An `__init__` method for the *default builder* if one is defined or inferred
876  for the operation.
877* `@property` getter for each operand or result (using an auto-generated name
878  for unnamed of each).
879* `@property` getter, setter and deleter for each declared attribute.
880
881It further emits additional private-intended attributes meant for subclassing
882and customization (default cases omit these attributes in favor of the
883defaults on `OpView`):
884
885* `_ODS_REGIONS`: A specification on the number and types of regions.
886  Currently a tuple of (min_region_count, has_no_variadic_regions). Note that
887  the API does some light validation on this but the primary purpose is to
888  capture sufficient information to perform other default building and region
889  accessor generation.
890* `_ODS_OPERAND_SEGMENTS` and `_ODS_RESULT_SEGMENTS`: Black-box value which
891  indicates the structure of either the operand or results with respect to
892  variadics. Used by `OpView._ods_build_default` to decode operand and result
893  lists that contain lists.
894
895#### Default Builder
896
897Presently, only a single, default builder is mapped to the `__init__` method.
898The intent is that this `__init__` method represents the *most specific* of
899the builders typically generated for C++; however currently it is just the
900generic form below.
901
902* One argument for each declared result:
903  * For single-valued results: Each will accept an `mlir.ir.Type`.
904  * For variadic results: Each will accept a `List[mlir.ir.Type]`.
905* One argument for each declared operand or attribute:
906  * For single-valued operands: Each will accept an `mlir.ir.Value`.
907  * For variadic operands: Each will accept a `List[mlir.ir.Value]`.
908  * For attributes, it will accept an `mlir.ir.Attribute`.
909* Trailing usage-specific, optional keyword arguments:
910  * `loc`: An explicit `mlir.ir.Location` to use. Defaults to the location
911    bound to the thread (i.e. `with Location.unknown():`) or an error if none
912    is bound nor specified.
913  * `ip`: An explicit `mlir.ir.InsertionPoint` to use. Default to the insertion
914    point bound to the thread (i.e. `with InsertionPoint(...):`).
915
916In addition, each `OpView` inherits a `build_generic` method which allows
917construction via a (nested in the case of variadic) sequence of `results` and
918`operands`. This can be used to get some default construction semantics for
919operations that are otherwise unsupported in Python, at the expense of having
920a very generic signature.
921
922#### Extending Generated Op Classes
923
924Note that this is a rather complex mechanism and this section errs on the side
925of explicitness. Users are encouraged to find an example and duplicate it if
926they don't feel the need to understand the subtlety. The `builtin` dialect
927provides some relatively simple examples.
928
929As mentioned above, the build system generates Python sources like
930`_{DIALECT_NAMESPACE}_ops_gen.py` for each dialect with Python bindings. It
931is often desirable to to use these generated classes as a starting point for
932further customization, so an extension mechanism is provided to make this
933easy (you are always free to do ad-hoc patching in your `{DIALECT_NAMESPACE}.py`
934file but we prefer a more standard mechanism that is applied uniformly).
935
936To provide extensions, add a `_{DIALECT_NAMESPACE}_ops_ext.py` file to the
937`dialects` module (i.e. adjacent to your `{DIALECT_NAMESPACE}.py` top-level
938and the `*_ops_gen.py` file). Using the `builtin` dialect and `FuncOp` as an
939example, the generated code will include an import like this:
940
941```python
942try:
943  from . import _builtin_ops_ext as _ods_ext_module
944except ImportError:
945  _ods_ext_module = None
946```
947
948Then for each generated concrete `OpView` subclass, it will apply a decorator
949like:
950
951```python
952@_ods_cext.register_operation(_Dialect)
953@_ods_extend_opview_class(_ods_ext_module)
954class FuncOp(_ods_ir.OpView):
955```
956
957See the `_ods_common.py` `extend_opview_class` function for details of the
958mechanism. At a high level:
959
960* If the extension module exists, locate an extension class for the op (in
961  this example, `FuncOp`):
962  * First by looking for an attribute with the exact name in the extension
963    module.
964  * Falling back to calling a `select_opview_mixin(parent_opview_cls)`
965    function defined in the extension module.
966* If a mixin class is found, a new subclass is dynamically created that multiply
967  inherits from `({_builtin_ops_ext.FuncOp}, _builtin_ops_gen.FuncOp)`.
968
969The mixin class should not inherit from anything (i.e. directly extends
970`object` only). The facility is typically used to define custom `__init__`
971methods, properties, instance methods and static methods. Due to the
972inheritance ordering, the mixin class can act as though it extends the
973generated `OpView` subclass in most contexts (i.e.
974`issubclass(_builtin_ops_ext.FuncOp, OpView)` will return `False` but usage
975generally allows you treat it as duck typed as an `OpView`).
976
977There are a couple of recommendations, given how the class hierarchy is
978defined:
979
980* For static methods that need to instantiate the actual "leaf" op (which
981  is dynamically generated and would result in circular dependencies to try
982  to reference by name), prefer to use `@classmethod` and the concrete
983  subclass will be provided as your first `cls` argument. See
984  `_builtin_ops_ext.FuncOp.from_py_func` as an example.
985* If seeking to replace the generated `__init__` method entirely, you may
986  actually want to invoke the super-super-class `mlir.ir.OpView` constructor
987  directly, as it takes an `mlir.ir.Operation`, which is likely what you
988  are constructing (i.e. the generated `__init__` method likely adds more
989  API constraints than you want to expose in a custom builder).
990
991A pattern that comes up frequently is wanting to provide a sugared `__init__`
992method which has optional or type-polymorphism/implicit conversions but to
993otherwise want to invoke the default op building logic. For such cases,
994it is recommended to use an idiom such as:
995
996```python
997  def __init__(self, sugar, spice, *, loc=None, ip=None):
998    ... massage into result_type, operands, attributes ...
999    OpView.__init__(self, self.build_generic(
1000        results=[result_type],
1001        operands=operands,
1002        attributes=attributes,
1003        loc=loc,
1004        ip=ip))
1005```
1006
1007Refer to the documentation for `build_generic` for more information.
1008