[Mlir-commits] [mlir] [MLIR][Python] Add Python and C API of `mlir::DynamicType` (PR #182751)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sun Feb 22 07:55:01 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Twice (PragmaTwice)

<details>
<summary>Changes</summary>

This PR adds C and Python API support for `mlir::DynamicType`. It primarily enables types in dialects that are dynamically generated via IRDL to be constructed in Python, and allows retrieving the parameters contained in a dynamic type from Python.


---
Full diff: https://github.com/llvm/llvm-project/pull/182751.diff


5 Files Affected:

- (modified) mlir/include/mlir-c/ExtensibleDialect.h (+28) 
- (modified) mlir/include/mlir/Bindings/Python/IRTypes.h (+12) 
- (modified) mlir/lib/Bindings/Python/IRTypes.cpp (+57) 
- (modified) mlir/lib/CAPI/IR/ExtensibleDialect.cpp (+38) 
- (modified) mlir/test/python/dialects/irdl.py (+71) 


``````````diff
diff --git a/mlir/include/mlir-c/ExtensibleDialect.h b/mlir/include/mlir-c/ExtensibleDialect.h
index 6901b2ec15d37..bfc06d7f10d57 100644
--- a/mlir/include/mlir-c/ExtensibleDialect.h
+++ b/mlir/include/mlir-c/ExtensibleDialect.h
@@ -32,6 +32,7 @@ extern "C" {
   typedef struct name name
 
 DEFINE_C_API_STRUCT(MlirDynamicOpTrait, void);
+DEFINE_C_API_STRUCT(MlirDynamicTypeDefinition, void);
 
 #undef DEFINE_C_API_STRUCT
 
@@ -73,6 +74,33 @@ typedef struct {
 MLIR_CAPI_EXPORTED MlirDynamicOpTrait mlirDynamicOpTraitCreate(
     MlirTypeID typeID, MlirDynamicOpTraitCallbacks callbacks, void *userData);
 
+/// Check if the given dialect is an extensible dialect.
+MLIR_CAPI_EXPORTED bool mlirDialectIsAExtensibleDialect(MlirDialect dialect);
+
+/// Look up a registered type definition by type name in the given dialect.
+/// Note that the dialect must be an extensible dialect.
+MLIR_CAPI_EXPORTED MlirDynamicTypeDefinition
+mlirExtensibleDialectLookupTypeDefinition(MlirDialect dialect,
+                                          MlirStringRef typeName);
+
+/// Check if the given type is a dynamic type.
+MLIR_CAPI_EXPORTED bool mlirTypeIsADynamicType(MlirType type);
+
+/// Get the type ID of a dynamic type.
+MLIR_CAPI_EXPORTED MlirTypeID mlirDynamicTypeGetTypeID(void);
+
+/// Get a dynamic type by instantiating the given type definition with the
+/// provided attributes.
+MLIR_CAPI_EXPORTED MlirType mlirDynamicTypeGet(
+    MlirDynamicTypeDefinition typeDef, MlirAttribute *attrs, intptr_t numAttrs);
+
+/// Get the number of parameters in the given dynamic type.
+MLIR_CAPI_EXPORTED intptr_t mlirDynamicTypeGetNumParams(MlirType type);
+
+/// Get the parameter at the given index in the provided dynamic type.
+MLIR_CAPI_EXPORTED MlirAttribute mlirDynamicTypeGetParam(MlirType type,
+                                                         intptr_t index);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/mlir/include/mlir/Bindings/Python/IRTypes.h b/mlir/include/mlir/Bindings/Python/IRTypes.h
index 4a8c5904b55c8..b29483ef78e5d 100644
--- a/mlir/include/mlir/Bindings/Python/IRTypes.h
+++ b/mlir/include/mlir/Bindings/Python/IRTypes.h
@@ -446,6 +446,18 @@ class MLIR_PYTHON_API_EXPORTED PyOpaqueType
   static void bindDerived(ClassTy &c);
 };
 
+class MLIR_PYTHON_API_EXPORTED PyDynamicType
+    : public PyConcreteType<PyDynamicType> {
+public:
+  static constexpr IsAFunctionTy isaFunction = mlirTypeIsADynamicType;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirDynamicTypeGetTypeID;
+  static constexpr const char *pyClassName = "DynamicType";
+  using PyConcreteType::PyConcreteType;
+
+  static void bindDerived(ClassTy &c);
+};
+
 MLIR_PYTHON_API_EXPORTED void populateIRTypes(nanobind::module_ &m);
 } // namespace MLIR_BINDINGS_PYTHON_DOMAIN
 } // namespace python
diff --git a/mlir/lib/Bindings/Python/IRTypes.cpp b/mlir/lib/Bindings/Python/IRTypes.cpp
index ec1c223c99f22..f0a8b0b0bfb86 100644
--- a/mlir/lib/Bindings/Python/IRTypes.cpp
+++ b/mlir/lib/Bindings/Python/IRTypes.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // clang-format off
+#include "mlir-c/ExtensibleDialect.h"
 #include "mlir/Bindings/Python/IRCore.h"
 #include "mlir/Bindings/Python/IRTypes.h"
 // clang-format on
@@ -841,6 +842,61 @@ void PyOpaqueType::bindDerived(ClassTy &c) {
       "Returns the data for the Opaque type as a string.");
 }
 
+void PyDynamicType::bindDerived(ClassTy &c) {
+  c.def_static(
+      "get",
+      [](const std::string &fullTypeName, const std::vector<PyAttribute> &attrs,
+         DefaultingPyMlirContext context) {
+        size_t dotPos = fullTypeName.find('.');
+        if (dotPos == std::string::npos) {
+          throw nb::value_error("Expected full type name to be in the format "
+                                "'<dialectName>.<typeName>'.");
+        }
+
+        std::string dialectName = fullTypeName.substr(0, dotPos);
+        std::string typeName = fullTypeName.substr(dotPos + 1);
+        PyDialects dialects(context->getRef());
+        MlirDialect dialect = dialects.getDialectForKey(dialectName, false);
+        if (!mlirDialectIsAExtensibleDialect(dialect)) {
+          throw nb::value_error(
+              ("Dialect '" + dialectName + "' is not an extensible dialect.")
+                  .c_str());
+        }
+
+        MlirDynamicTypeDefinition typeDef =
+            mlirExtensibleDialectLookupTypeDefinition(
+                dialect, toMlirStringRef(typeName));
+        if (typeDef.ptr == nullptr) {
+          throw nb::value_error(("Dialect '" + dialectName +
+                                 "' does not contain a type named '" +
+                                 typeName + "'.")
+                                    .c_str());
+        }
+
+        std::vector<MlirAttribute> mlirAttrs;
+        mlirAttrs.reserve(attrs.size());
+        for (const auto &attr : attrs)
+          mlirAttrs.push_back(attr.get());
+        MlirType t =
+            mlirDynamicTypeGet(typeDef, mlirAttrs.data(), mlirAttrs.size());
+        return PyDynamicType(context->getRef(), t);
+      },
+      nb::arg("full_type_name"), nb::arg("attributes"),
+      nb::arg("context") = nb::none(), "Create a dynamic type.");
+  c.def_prop_ro(
+      "params",
+      [](PyDynamicType &self) {
+        size_t numParams = mlirDynamicTypeGetNumParams(self);
+        std::vector<PyAttribute> params;
+        params.reserve(numParams);
+        for (size_t i = 0; i < numParams; ++i)
+          params.emplace_back(self.getContext(),
+                              mlirDynamicTypeGetParam(self, i));
+        return params;
+      },
+      "Returns the parameters of the dynamic type as a list of attributes.");
+}
+
 void populateIRTypes(nb::module_ &m) {
   PyIntegerType::bind(m);
   PyFloatType::bind(m);
@@ -872,6 +928,7 @@ void populateIRTypes(nb::module_ &m) {
   PyTupleType::bind(m);
   PyFunctionType::bind(m);
   PyOpaqueType::bind(m);
+  PyDynamicType::bind(m);
 }
 } // namespace MLIR_BINDINGS_PYTHON_DOMAIN
 } // namespace python
diff --git a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
index 2d6608ff1d1cd..cec7e6300721a 100644
--- a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
+++ b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
@@ -15,6 +15,7 @@
 using namespace mlir;
 
 DEFINE_C_API_PTR_METHODS(MlirDynamicOpTrait, DynamicOpTrait)
+DEFINE_C_API_PTR_METHODS(MlirDynamicTypeDefinition, DynamicTypeDefinition)
 
 bool mlirDynamicOpTraitAttach(MlirDynamicOpTrait dynamicOpTrait,
                               MlirStringRef opName, MlirContext context) {
@@ -85,3 +86,40 @@ MlirDynamicOpTrait mlirDynamicOpTraitCreate(
   return wrap(
       new mlir::ExternalDynamicOpTrait(unwrap(typeID), callbacks, userData));
 }
+
+bool mlirDialectIsAExtensibleDialect(MlirDialect dialect) {
+  return llvm::isa<mlir::ExtensibleDialect>(unwrap(dialect));
+}
+
+MlirDynamicTypeDefinition
+mlirExtensibleDialectLookupTypeDefinition(MlirDialect dialect,
+                                          MlirStringRef typeName) {
+  return wrap(llvm::cast<mlir::ExtensibleDialect>(unwrap(dialect))
+                  ->lookupTypeDefinition(unwrap(typeName)));
+}
+
+bool mlirTypeIsADynamicType(MlirType type) {
+  return llvm::isa<mlir::DynamicType>(unwrap(type));
+}
+
+MlirTypeID mlirDynamicTypeGetTypeID() {
+  return wrap(mlir::DynamicType::getTypeID());
+}
+
+MlirType mlirDynamicTypeGet(MlirDynamicTypeDefinition typeDef,
+                            MlirAttribute *attrs, intptr_t numAttrs) {
+  llvm::SmallVector<mlir::Attribute> attributes;
+  attributes.reserve(numAttrs);
+  for (intptr_t i = 0; i < numAttrs; ++i)
+    attributes.push_back(unwrap(attrs[i]));
+
+  return wrap(mlir::DynamicType::get(unwrap(typeDef), attributes));
+}
+
+intptr_t mlirDynamicTypeGetNumParams(MlirType type) {
+  return llvm::cast<mlir::DynamicType>(unwrap(type)).getParams().size();
+}
+
+MlirAttribute mlirDynamicTypeGetParam(MlirType type, intptr_t index) {
+  return wrap(llvm::cast<mlir::DynamicType>(unwrap(type)).getParams()[index]);
+}
diff --git a/mlir/test/python/dialects/irdl.py b/mlir/test/python/dialects/irdl.py
index ed62db9b69968..369065047b6e2 100644
--- a/mlir/test/python/dialects/irdl.py
+++ b/mlir/test/python/dialects/irdl.py
@@ -64,3 +64,74 @@ def testIRDL():
         # CHECK:   "irdl_test.test_op"(%cst) : (f32) -> ()
         # CHECK: }
         m.dump()
+
+
+ at run
+def testIRDLTypes():
+    with Context() as ctx, Location.unknown():
+        module = Module.create()
+        with InsertionPoint(module.body):
+            irdl_test = dialect("irdl_type_test")
+            with InsertionPoint(irdl_test.body):
+                type1 = type_("type1")
+                with InsertionPoint(type1.body):
+                    iattr = base(base_name="#builtin.integer")
+                    parameters([iattr], ["val"])
+                type2 = type_("type2")
+                with InsertionPoint(type2.body):
+                    iattr = base(base_name="#builtin.integer")
+                    unit = is_(UnitAttr.get())
+                    parameters([iattr, unit], ["val1", "val2"])
+                op1 = operation_("op1")
+                with InsertionPoint(op1.body):
+                    t1 = base(base_ref=["irdl_type_test", "type1"])
+                    results_([t1], ["res"], [Variadicity.single])
+
+        # CHECK: module {
+        # CHECK:   irdl.dialect @irdl_type_test {
+        # CHECK:     irdl.type @type1 {
+        # CHECK:       %0 = irdl.base "#builtin.integer"
+        # CHECK:       irdl.parameters(val: %0)
+        # CHECK:     }
+        # CHECK:     irdl.type @type2 {
+        # CHECK:       %0 = irdl.base "#builtin.integer"
+        # CHECK:       %1 = irdl.is unit
+        # CHECK:       irdl.parameters(val1: %0, val2: %1)
+        # CHECK:     }
+        # CHECK:     irdl.operation @op1 {
+        # CHECK:       %0 = irdl.base @irdl_type_test::@type1
+        # CHECK:       irdl.results(res: %0)
+        # CHECK:     }
+        # CHECK:   }
+        # CHECK: }
+        module.operation.verify()
+        module.dump()
+
+        load_dialects(module)
+
+        i32 = IntegerType.get(32)
+        t1 = DynamicType.get("irdl_type_test.type1", [IntegerAttr.get(i32, 42)])
+        # CHECK: !irdl_type_test.type1<42 : i32>
+        t1.dump()
+        # CHECK: 1
+        print(len(t1.params), file=sys.stderr)
+        # CHECK: 42 : i32
+        t1.params[0].dump()
+        t2 = DynamicType.get(
+            "irdl_type_test.type2", [IntegerAttr.get(i32, 33), UnitAttr.get()]
+        )
+        # CHECK: !irdl_type_test.type2<33 : i32, unit>
+        t2.dump()
+        # CHECK: 2
+        print(len(t2.params), file=sys.stderr)
+        # CHECK: 33 : i32
+        t2.params[0].dump()
+        # CHECK: unit
+        t2.params[1].dump()
+
+        m = Module.create()
+        with InsertionPoint(m.body):
+            Operation.create("irdl_type_test.op1", results=[t1])
+
+        # CHECK: %0 = "irdl_type_test.op1"() : () -> !irdl_type_test.type1<42 : i32>
+        m.dump()

``````````

</details>


https://github.com/llvm/llvm-project/pull/182751


More information about the Mlir-commits mailing list