RFC: Defining and extending MLIR dialects dynamically

Currently, all operations, types, and dialects have to be defined at compile-time, either in C++ or TableGen. While there is interest for some users in defining them at runtime (from Python or Swift for instance), there is currently no practical way to do so.

I presented this RFC at the last ODM.

Scope and goals

The goal of this RFC is to provide the right API in MLIR to define and register new operations / types / attributes / dialects at runtime. This includes defining at runtime parsers, printers, verifiers, traits, and interface implementations.

Current implementation design

In MLIR, each defined op is a child class of Op. For runtime-defined operations, each runtime operation would be an instance of DynamicOpDefinition. Each runtime operation is still given a unique TypeID, which is generated at runtime. It is possible to query traits or interface implementations the same way as this is done in MLIR, which means that we can reuse current passes with runtime-defined operations.

DynamicTypes and DynamicAttributes work the same way. DynamicType is parameterized with a list of attributes, and DynamicAttribute contains also a list of attributes. Note that this way, dynamic types and attributes can represent arbitrary data.

Dialects can register new DynamicOps or DynamicTypes by inheriting from the ExtensibleDialect class, which can simply be done in TableGen. Dialects can also be defined at runtime with the DynamicDialect class, which also inherits from ExtensibleDialect.

Traits may also be defined at runtime, by just providing a name and a verifier function.

We cannot really define new interfaces at runtime, but we can use them on runtime-defined operations or types. However, there is an important choice to make on the implementation (which I would like to have your feedback on):
Currently, interfaces are stored in operations or types in structs containing function pointers. In order to implement the interfaces at runtime, we would need to change those to unique_functions instead. This could have a performance impact, but when discussing it in the ODM, it seemed that most of the cost of interfaces comes from retrieving the interface, which wouldn’t change here. I have another working solution, but it would require to generate new structs for each defined interface.

Patches

I have a working implementation of most of the features, and I am currently making incremental patches. I already have two ready: The first one allows the definition of TypeIDs at runtime, and the second one adds extensible dialects, as well as dynamic types and dynamic operations.

Currently, what is missing from the patches is:

  • Support for building dynamic operations/types with the builder or rewriter (I’m working on this patch right now)

  • Support for types and interfaces

Thanks!

1 Like