[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 20:06:40 PST 2026
https://github.com/PragmaTwice updated https://github.com/llvm/llvm-project/pull/182751
>From 28e5faa7b7ddf92f19593fdb57978da8c8371115 Mon Sep 17 00:00:00 2001
From: PragmaTwice <twice at apache.org>
Date: Sun, 22 Feb 2026 23:49:48 +0800
Subject: [PATCH 1/3] [MLIR][Python] Add Python and C API of mlir::DynamicType
---
mlir/include/mlir-c/ExtensibleDialect.h | 28 ++++++++
mlir/include/mlir/Bindings/Python/IRTypes.h | 12 ++++
mlir/lib/Bindings/Python/IRTypes.cpp | 57 +++++++++++++++++
mlir/lib/CAPI/IR/ExtensibleDialect.cpp | 38 +++++++++++
mlir/test/python/dialects/irdl.py | 71 +++++++++++++++++++++
5 files changed, 206 insertions(+)
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()
>From 55eb54fb7333b43645f7264c746c2bccc4e2a132 Mon Sep 17 00:00:00 2001
From: PragmaTwice <twice at apache.org>
Date: Mon, 23 Feb 2026 12:06:07 +0800
Subject: [PATCH 2/3] add DynamicType.type_name
---
mlir/include/mlir-c/ExtensibleDialect.h | 12 ++++++++++++
mlir/lib/Bindings/Python/IRTypes.cpp | 8 ++++++++
mlir/lib/CAPI/IR/ExtensibleDialect.cpp | 14 ++++++++++++++
mlir/test/python/dialects/irdl.py | 4 ++++
4 files changed, 38 insertions(+)
diff --git a/mlir/include/mlir-c/ExtensibleDialect.h b/mlir/include/mlir-c/ExtensibleDialect.h
index bfc06d7f10d57..eabcd080c5d4b 100644
--- a/mlir/include/mlir-c/ExtensibleDialect.h
+++ b/mlir/include/mlir-c/ExtensibleDialect.h
@@ -101,6 +101,18 @@ MLIR_CAPI_EXPORTED intptr_t mlirDynamicTypeGetNumParams(MlirType type);
MLIR_CAPI_EXPORTED MlirAttribute mlirDynamicTypeGetParam(MlirType type,
intptr_t index);
+/// Get the type definition of the given dynamic type.
+MLIR_CAPI_EXPORTED MlirDynamicTypeDefinition
+mlirDynamicTypeGetTypeDef(MlirType type);
+
+/// Get the name of the given dynamic type definition.
+MLIR_CAPI_EXPORTED MlirStringRef
+mlirDynamicTypeDefinitionGetName(MlirDynamicTypeDefinition typeDef);
+
+/// Get the dialect that the given dynamic type definition belongs to.
+MLIR_CAPI_EXPORTED MlirDialect
+mlirDynamicTypeDefinitionGetDialect(MlirDynamicTypeDefinition typeDef);
+
#ifdef __cplusplus
}
#endif
diff --git a/mlir/lib/Bindings/Python/IRTypes.cpp b/mlir/lib/Bindings/Python/IRTypes.cpp
index f0a8b0b0bfb86..b86f9f91ace0a 100644
--- a/mlir/lib/Bindings/Python/IRTypes.cpp
+++ b/mlir/lib/Bindings/Python/IRTypes.cpp
@@ -895,6 +895,14 @@ void PyDynamicType::bindDerived(ClassTy &c) {
return params;
},
"Returns the parameters of the dynamic type as a list of attributes.");
+ c.def_prop_ro("type_name", [](PyDynamicType &self) {
+ MlirDynamicTypeDefinition typeDef = mlirDynamicTypeGetTypeDef(self);
+ MlirStringRef name = mlirDynamicTypeDefinitionGetName(typeDef);
+ MlirDialect dialect = mlirDynamicTypeDefinitionGetDialect(typeDef);
+ MlirStringRef dialectNamespace = mlirDialectGetNamespace(dialect);
+ return std::string(dialectNamespace.data, dialectNamespace.length) + "." +
+ std::string(name.data, name.length);
+ });
}
void populateIRTypes(nb::module_ &m) {
diff --git a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
index cec7e6300721a..1659b0afd7354 100644
--- a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
+++ b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
@@ -123,3 +123,17 @@ intptr_t mlirDynamicTypeGetNumParams(MlirType type) {
MlirAttribute mlirDynamicTypeGetParam(MlirType type, intptr_t index) {
return wrap(llvm::cast<mlir::DynamicType>(unwrap(type)).getParams()[index]);
}
+
+MlirDynamicTypeDefinition mlirDynamicTypeGetTypeDef(MlirType type) {
+ return wrap(llvm::cast<mlir::DynamicType>(unwrap(type)).getTypeDef());
+}
+
+MlirStringRef
+mlirDynamicTypeDefinitionGetName(MlirDynamicTypeDefinition typeDef) {
+ return wrap(unwrap(typeDef)->getName());
+}
+
+MlirDialect
+mlirDynamicTypeDefinitionGetDialect(MlirDynamicTypeDefinition typeDef) {
+ return wrap(unwrap(typeDef)->getDialect());
+}
diff --git a/mlir/test/python/dialects/irdl.py b/mlir/test/python/dialects/irdl.py
index 369065047b6e2..da5d51d1b082a 100644
--- a/mlir/test/python/dialects/irdl.py
+++ b/mlir/test/python/dialects/irdl.py
@@ -113,6 +113,8 @@ def testIRDLTypes():
t1 = DynamicType.get("irdl_type_test.type1", [IntegerAttr.get(i32, 42)])
# CHECK: !irdl_type_test.type1<42 : i32>
t1.dump()
+ # CHECK: irdl_type_test.type1
+ print(t1.type_name, file=sys.stderr)
# CHECK: 1
print(len(t1.params), file=sys.stderr)
# CHECK: 42 : i32
@@ -122,6 +124,8 @@ def testIRDLTypes():
)
# CHECK: !irdl_type_test.type2<33 : i32, unit>
t2.dump()
+ # CHECK: irdl_type_test.type2
+ print(t2.type_name, file=sys.stderr)
# CHECK: 2
print(len(t2.params), file=sys.stderr)
# CHECK: 33 : i32
>From 9b373a82b762c1e6ffa6ec79a4421e103de39cfd Mon Sep 17 00:00:00 2001
From: Twice <twice at apache.org>
Date: Mon, 23 Feb 2026 12:06:32 +0800
Subject: [PATCH 3/3] Update mlir/lib/Bindings/Python/IRTypes.cpp
Co-authored-by: Rolf Morel <rolfmorel at gmail.com>
---
mlir/lib/Bindings/Python/IRTypes.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/mlir/lib/Bindings/Python/IRTypes.cpp b/mlir/lib/Bindings/Python/IRTypes.cpp
index b86f9f91ace0a..aa448fa44d9bf 100644
--- a/mlir/lib/Bindings/Python/IRTypes.cpp
+++ b/mlir/lib/Bindings/Python/IRTypes.cpp
@@ -857,11 +857,10 @@ void PyDynamicType::bindDerived(ClassTy &c) {
std::string typeName = fullTypeName.substr(dotPos + 1);
PyDialects dialects(context->getRef());
MlirDialect dialect = dialects.getDialectForKey(dialectName, false);
- if (!mlirDialectIsAExtensibleDialect(dialect)) {
+ if (!mlirDialectIsAExtensibleDialect(dialect))
throw nb::value_error(
("Dialect '" + dialectName + "' is not an extensible dialect.")
.c_str());
- }
MlirDynamicTypeDefinition typeDef =
mlirExtensibleDialectLookupTypeDefinition(
More information about the Mlir-commits
mailing list