[Mlir-commits] [mlir] [mlir][CAPI] Add C API for creating dynamic dialects, ops, types, and attrs (PR #187239)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Mar 19 12:15:40 PDT 2026
https://github.com/edg-l updated https://github.com/llvm/llvm-project/pull/187239
>From 5b7171969c59747c3e5c8df9e6abf8240c3bf7cb Mon Sep 17 00:00:00 2001
From: Edgar Luque <git at edgl.dev>
Date: Wed, 18 Mar 2026 11:37:56 +0100
Subject: [PATCH 1/2] [mlir][CAPI] Add C API for creating dynamic dialects,
ops, types, and attrs
The existing ExtensibleDialect C API exposes functions for looking up
and instantiating dynamic types/attrs, and for attaching traits to
dynamic ops. However, there is no way to *create* a DynamicDialect,
DynamicOpDefinition, DynamicTypeDefinition, or DynamicAttrDefinition
from C.
This patch adds the missing creation APIs:
- mlirDynamicDialectCreate: wraps MLIRContext::getOrLoadDynamicDialect
- mlirDynamicOpDefinitionCreate + mlirDynamicDialectRegisterOp
- mlirDynamicTypeDefinitionCreate + mlirDynamicDialectRegisterType
- mlirDynamicAttrDefinitionCreate + mlirDynamicDialectRegisterAttr
Op definitions accept C callback functions for verify and verifyRegion.
Type and attribute definitions accept C callback functions for parameter
verification.
This enables languages with C FFI (Rust, Zig, etc.) to define complete
MLIR dialects at runtime without C++ code.
---
mlir/include/mlir-c/ExtensibleDialect.h | 116 ++++++++++++++++
mlir/lib/CAPI/IR/ExtensibleDialect.cpp | 129 ++++++++++++++++++
mlir/test/CAPI/CMakeLists.txt | 6 +
mlir/test/CAPI/extensible_dialect.c | 172 ++++++++++++++++++++++++
mlir/test/CMakeLists.txt | 1 +
5 files changed, 424 insertions(+)
create mode 100644 mlir/test/CAPI/extensible_dialect.c
diff --git a/mlir/include/mlir-c/ExtensibleDialect.h b/mlir/include/mlir-c/ExtensibleDialect.h
index fee26772e1560..c0770a8920981 100644
--- a/mlir/include/mlir-c/ExtensibleDialect.h
+++ b/mlir/include/mlir-c/ExtensibleDialect.h
@@ -31,12 +31,128 @@ extern "C" {
}; \
typedef struct name name
+DEFINE_C_API_STRUCT(MlirDynamicDialect, void);
+DEFINE_C_API_STRUCT(MlirDynamicOpDefinition, void);
DEFINE_C_API_STRUCT(MlirDynamicOpTrait, void);
DEFINE_C_API_STRUCT(MlirDynamicTypeDefinition, void);
DEFINE_C_API_STRUCT(MlirDynamicAttrDefinition, void);
#undef DEFINE_C_API_STRUCT
+//===----------------------------------------------------------------------===//
+/// Dynamic dialect creation
+//===----------------------------------------------------------------------===//
+
+/// Create a new dynamic dialect with the given name and register it with the
+/// context. If a dialect with the same name already exists, returns the
+/// existing one. The context takes ownership of the dialect. The returned
+/// handle is valid as long as the context is alive and must not be freed.
+MLIR_CAPI_EXPORTED MlirDynamicDialect
+mlirDynamicDialectCreate(MlirContext ctx, MlirStringRef name);
+
+/// Get the underlying MlirDialect from a MlirDynamicDialect.
+MLIR_CAPI_EXPORTED MlirDialect
+mlirDynamicDialectGetDialect(MlirDynamicDialect dialect);
+
+//===----------------------------------------------------------------------===//
+/// Dynamic op definition creation
+//===----------------------------------------------------------------------===//
+
+/// Callback to verify a dynamic op.
+typedef MlirLogicalResult (*MlirDynamicOpVerifyFn)(MlirOperation op,
+ void *userData);
+
+/// Create a dynamic op definition with the given name and verifier callbacks.
+/// The name should be the bare op name (e.g. "constant"), not the
+/// dialect-qualified name. The definition is NOT yet registered with the
+/// dialect; call mlirDynamicDialectRegisterOp to register it.
+/// Both \p verifyFn and \p verifyRegionFn may be NULL, in which case
+/// verification always succeeds. \p userData is shared between both callbacks.
+/// If the definition is not registered, it must be destroyed with
+/// mlirDynamicOpDefinitionDestroy to avoid a memory leak.
+MLIR_CAPI_EXPORTED MlirDynamicOpDefinition mlirDynamicOpDefinitionCreate(
+ MlirDynamicDialect dialect, MlirStringRef name,
+ MlirDynamicOpVerifyFn verifyFn, MlirDynamicOpVerifyFn verifyRegionFn,
+ void *userData);
+
+/// Destroy a dynamic op definition that was not registered with a dialect.
+MLIR_CAPI_EXPORTED void
+mlirDynamicOpDefinitionDestroy(MlirDynamicOpDefinition opDef);
+
+/// Register a dynamic op definition with its parent dialect.
+/// This transfers ownership of the definition to the dialect.
+/// After this call, ops with this name can be created using
+/// mlirOperationCreate with the dialect-qualified name.
+MLIR_CAPI_EXPORTED void
+mlirDynamicDialectRegisterOp(MlirDynamicDialect dialect,
+ MlirDynamicOpDefinition opDef);
+
+//===----------------------------------------------------------------------===//
+/// Dynamic type definition creation
+//===----------------------------------------------------------------------===//
+
+/// Callback to verify a dynamic type's parameters.
+typedef MlirLogicalResult (*MlirDynamicTypeVerifyFn)(
+ intptr_t nParams, MlirAttribute const *params, void *userData);
+
+/// Create a dynamic type definition with the given name and verifier.
+/// The name should be the bare type name (e.g. "mystruct"), not the
+/// dialect-qualified name. The definition is NOT yet registered with the
+/// dialect; call mlirDynamicDialectRegisterType to register it.
+/// \p verifyFn may be NULL, in which case verification always succeeds.
+/// Note: the verifier callback does not receive diagnostic context; failures
+/// are reported without a custom error message.
+/// If the definition is not registered, it must be destroyed with
+/// mlirDynamicTypeDefinitionDestroy to avoid a memory leak.
+MLIR_CAPI_EXPORTED MlirDynamicTypeDefinition mlirDynamicTypeDefinitionCreate(
+ MlirDynamicDialect dialect, MlirStringRef name,
+ MlirDynamicTypeVerifyFn verifyFn, void *userData);
+
+/// Destroy a dynamic type definition that was not registered with a dialect.
+MLIR_CAPI_EXPORTED void
+mlirDynamicTypeDefinitionDestroy(MlirDynamicTypeDefinition typeDef);
+
+/// Register a dynamic type definition with its parent dialect.
+/// This transfers ownership of the definition to the dialect.
+MLIR_CAPI_EXPORTED void
+mlirDynamicDialectRegisterType(MlirDynamicDialect dialect,
+ MlirDynamicTypeDefinition typeDef);
+
+//===----------------------------------------------------------------------===//
+/// Dynamic attribute definition creation
+//===----------------------------------------------------------------------===//
+
+/// Callback to verify a dynamic attribute's parameters.
+typedef MlirLogicalResult (*MlirDynamicAttrVerifyFn)(
+ intptr_t nParams, MlirAttribute const *params, void *userData);
+
+/// Create a dynamic attribute definition with the given name and verifier.
+/// The name should be the bare attr name, not the dialect-qualified name.
+/// The definition is NOT yet registered with the dialect; call
+/// mlirDynamicDialectRegisterAttr to register it.
+/// \p verifyFn may be NULL, in which case verification always succeeds.
+/// Note: the verifier callback does not receive diagnostic context; failures
+/// are reported without a custom error message.
+/// If the definition is not registered, it must be destroyed with
+/// mlirDynamicAttrDefinitionDestroy to avoid a memory leak.
+MLIR_CAPI_EXPORTED MlirDynamicAttrDefinition mlirDynamicAttrDefinitionCreate(
+ MlirDynamicDialect dialect, MlirStringRef name,
+ MlirDynamicAttrVerifyFn verifyFn, void *userData);
+
+/// Destroy a dynamic attr definition that was not registered with a dialect.
+MLIR_CAPI_EXPORTED void
+mlirDynamicAttrDefinitionDestroy(MlirDynamicAttrDefinition attrDef);
+
+/// Register a dynamic attribute definition with its parent dialect.
+/// This transfers ownership of the definition to the dialect.
+MLIR_CAPI_EXPORTED void
+mlirDynamicDialectRegisterAttr(MlirDynamicDialect dialect,
+ MlirDynamicAttrDefinition attrDef);
+
+//===----------------------------------------------------------------------===//
+/// Dynamic op trait APIs
+//===----------------------------------------------------------------------===//
+
/// Attach a dynamic op trait to the given operation name.
/// Note that the operation name must be modeled by dynamic dialect and must be
/// registered.
diff --git a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
index 60a7f7d9064eb..2178196dbb232 100644
--- a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
+++ b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
@@ -9,15 +9,144 @@
#include "mlir-c/ExtensibleDialect.h"
#include "mlir/CAPI/IR.h"
#include "mlir/CAPI/Support.h"
+#include "mlir/CAPI/Wrap.h"
#include "mlir/IR/ExtensibleDialect.h"
+#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/OperationSupport.h"
using namespace mlir;
+DEFINE_C_API_PTR_METHODS(MlirDynamicDialect, DynamicDialect)
+DEFINE_C_API_PTR_METHODS(MlirDynamicOpDefinition, DynamicOpDefinition)
DEFINE_C_API_PTR_METHODS(MlirDynamicOpTrait, DynamicOpTrait)
DEFINE_C_API_PTR_METHODS(MlirDynamicTypeDefinition, DynamicTypeDefinition)
DEFINE_C_API_PTR_METHODS(MlirDynamicAttrDefinition, DynamicAttrDefinition)
+//===----------------------------------------------------------------------===//
+// Dynamic dialect creation
+//===----------------------------------------------------------------------===//
+
+MlirDynamicDialect mlirDynamicDialectCreate(MlirContext ctx,
+ MlirStringRef name) {
+ DynamicDialect *dialect = unwrap(ctx)->getOrLoadDynamicDialect(
+ unwrap(name), [](DynamicDialect *) {});
+ return wrap(dialect);
+}
+
+MlirDialect mlirDynamicDialectGetDialect(MlirDynamicDialect dialect) {
+ return wrap(static_cast<Dialect *>(unwrap(dialect)));
+}
+
+//===----------------------------------------------------------------------===//
+// Dynamic op definition creation
+//===----------------------------------------------------------------------===//
+
+MlirDynamicOpDefinition
+mlirDynamicOpDefinitionCreate(MlirDynamicDialect dialect, MlirStringRef name,
+ MlirDynamicOpVerifyFn verifyFn,
+ MlirDynamicOpVerifyFn verifyRegionFn,
+ void *userData) {
+ auto cppVerifyFn = [verifyFn, userData](Operation *op) -> LogicalResult {
+ if (!verifyFn)
+ return success();
+ return unwrap(verifyFn(wrap(op), userData));
+ };
+ auto cppVerifyRegionFn = [verifyRegionFn,
+ userData](Operation *op) -> LogicalResult {
+ if (!verifyRegionFn)
+ return success();
+ return unwrap(verifyRegionFn(wrap(op), userData));
+ };
+ std::unique_ptr<DynamicOpDefinition> opDef = DynamicOpDefinition::get(
+ unwrap(name), unwrap(dialect), std::move(cppVerifyFn),
+ std::move(cppVerifyRegionFn));
+ return wrap(opDef.release());
+}
+
+void mlirDynamicOpDefinitionDestroy(MlirDynamicOpDefinition opDef) {
+ delete unwrap(opDef);
+}
+
+void mlirDynamicDialectRegisterOp(MlirDynamicDialect dialect,
+ MlirDynamicOpDefinition opDef) {
+ unwrap(dialect)->registerDynamicOp(
+ std::unique_ptr<DynamicOpDefinition>(unwrap(opDef)));
+}
+
+//===----------------------------------------------------------------------===//
+// Dynamic type definition creation
+//===----------------------------------------------------------------------===//
+
+MlirDynamicTypeDefinition
+mlirDynamicTypeDefinitionCreate(MlirDynamicDialect dialect, MlirStringRef name,
+ MlirDynamicTypeVerifyFn verifyFn,
+ void *userData) {
+ auto cppVerifyFn = [verifyFn,
+ userData](function_ref<InFlightDiagnostic()> emitError,
+ ArrayRef<Attribute> params) -> LogicalResult {
+ if (!verifyFn)
+ return success();
+ SmallVector<MlirAttribute> cParams;
+ cParams.reserve(params.size());
+ for (Attribute p : params)
+ cParams.push_back(wrap(p));
+ return unwrap(verifyFn(static_cast<intptr_t>(cParams.size()),
+ cParams.data(), userData));
+ };
+ std::unique_ptr<DynamicTypeDefinition> typeDef = DynamicTypeDefinition::get(
+ unwrap(name), unwrap(dialect), std::move(cppVerifyFn));
+ return wrap(typeDef.release());
+}
+
+void mlirDynamicTypeDefinitionDestroy(MlirDynamicTypeDefinition typeDef) {
+ delete unwrap(typeDef);
+}
+
+void mlirDynamicDialectRegisterType(MlirDynamicDialect dialect,
+ MlirDynamicTypeDefinition typeDef) {
+ unwrap(dialect)->registerDynamicType(
+ std::unique_ptr<DynamicTypeDefinition>(unwrap(typeDef)));
+}
+
+//===----------------------------------------------------------------------===//
+// Dynamic attribute definition creation
+//===----------------------------------------------------------------------===//
+
+MlirDynamicAttrDefinition
+mlirDynamicAttrDefinitionCreate(MlirDynamicDialect dialect, MlirStringRef name,
+ MlirDynamicAttrVerifyFn verifyFn,
+ void *userData) {
+ auto cppVerifyFn = [verifyFn,
+ userData](function_ref<InFlightDiagnostic()> emitError,
+ ArrayRef<Attribute> params) -> LogicalResult {
+ if (!verifyFn)
+ return success();
+ SmallVector<MlirAttribute> cParams;
+ cParams.reserve(params.size());
+ for (Attribute p : params)
+ cParams.push_back(wrap(p));
+ return unwrap(verifyFn(static_cast<intptr_t>(cParams.size()),
+ cParams.data(), userData));
+ };
+ std::unique_ptr<DynamicAttrDefinition> attrDef = DynamicAttrDefinition::get(
+ unwrap(name), unwrap(dialect), std::move(cppVerifyFn));
+ return wrap(attrDef.release());
+}
+
+void mlirDynamicAttrDefinitionDestroy(MlirDynamicAttrDefinition attrDef) {
+ delete unwrap(attrDef);
+}
+
+void mlirDynamicDialectRegisterAttr(MlirDynamicDialect dialect,
+ MlirDynamicAttrDefinition attrDef) {
+ unwrap(dialect)->registerDynamicAttr(
+ std::unique_ptr<DynamicAttrDefinition>(unwrap(attrDef)));
+}
+
+//===----------------------------------------------------------------------===//
+// Dynamic op trait APIs
+//===----------------------------------------------------------------------===//
+
bool mlirDynamicOpTraitAttach(MlirDynamicOpTrait dynamicOpTrait,
MlirStringRef opName, MlirContext context) {
std::optional<RegisteredOperationName> opNameFound =
diff --git a/mlir/test/CAPI/CMakeLists.txt b/mlir/test/CAPI/CMakeLists.txt
index d45142510a496..b8509674c5b87 100644
--- a/mlir/test/CAPI/CMakeLists.txt
+++ b/mlir/test/CAPI/CMakeLists.txt
@@ -48,6 +48,12 @@ _add_capi_test_executable(mlir-capi-ir-test
MLIRCAPIRegisterEverything
)
+_add_capi_test_executable(mlir-capi-extensible-dialect-test
+ extensible_dialect.c
+ LINK_LIBS PRIVATE
+ MLIRCAPIIR
+)
+
_add_capi_test_executable(mlir-capi-irdl-test
irdl.c
LINK_LIBS PRIVATE
diff --git a/mlir/test/CAPI/extensible_dialect.c b/mlir/test/CAPI/extensible_dialect.c
new file mode 100644
index 0000000000000..cf1706f10ba84
--- /dev/null
+++ b/mlir/test/CAPI/extensible_dialect.c
@@ -0,0 +1,172 @@
+//===- extensible_dialect.c - Test C API for extensible dialect creation
+//---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM
+// Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+/* RUN: mlir-capi-extensible-dialect-test 2>&1 | FileCheck %s
+ */
+
+#include "mlir-c/BuiltinAttributes.h"
+#include "mlir-c/BuiltinTypes.h"
+#include "mlir-c/ExtensibleDialect.h"
+#include "mlir-c/IR.h"
+#include "mlir-c/Support.h"
+
+#include <stdio.h>
+
+static MlirStringRef strref(const char *s) {
+ return mlirStringRefCreateFromCString(s);
+}
+
+static MlirLogicalResult successTypeVerify(intptr_t nParams,
+ MlirAttribute const *params,
+ void *userData) {
+ (void)nParams;
+ (void)params;
+ (void)userData;
+ return mlirLogicalResultSuccess();
+}
+
+static MlirLogicalResult successAttrVerify(intptr_t nParams,
+ MlirAttribute const *params,
+ void *userData) {
+ (void)nParams;
+ (void)params;
+ (void)userData;
+ return mlirLogicalResultSuccess();
+}
+
+void testDynamicDialectCreation(MlirContext ctx) {
+ // Create a dynamic dialect.
+ MlirDynamicDialect testDialect =
+ mlirDynamicDialectCreate(ctx, strref("test_dyn"));
+ MlirDialect dialect = mlirDynamicDialectGetDialect(testDialect);
+
+ // CHECK: test_dyn is extensible: 1
+ fprintf(stderr, "test_dyn is extensible: %d\n",
+ mlirDialectIsAExtensibleDialect(dialect));
+}
+
+void testDynamicOpCreation(MlirContext ctx) {
+ MlirDynamicDialect testDialect =
+ mlirDynamicDialectCreate(ctx, strref("testop"));
+
+ // Define and register a simple op with NULL verifiers.
+ MlirDynamicOpDefinition opDef = mlirDynamicOpDefinitionCreate(
+ testDialect, strref("my_op"), NULL, NULL, NULL);
+ mlirDynamicDialectRegisterOp(testDialect, opDef);
+
+ // Parse and dump IR that uses this op.
+ const char *irStr = "module { \"testop.my_op\"() : () -> () }";
+ MlirModule module = mlirModuleCreateParse(ctx, strref(irStr));
+ if (mlirModuleIsNull(module)) {
+ fprintf(stderr, "failed to parse module\n");
+ return;
+ }
+
+ // CHECK: module {
+ // CHECK-NEXT: "testop.my_op"() : () -> ()
+ // CHECK-NEXT: }
+ mlirOperationDump(mlirModuleGetOperation(module));
+ mlirModuleDestroy(module);
+}
+
+void testDynamicTypeCreation(MlirContext ctx) {
+ MlirDynamicDialect testDialect =
+ mlirDynamicDialectCreate(ctx, strref("testtype"));
+
+ // Define and register a dynamic type.
+ MlirDynamicTypeDefinition typeDef = mlirDynamicTypeDefinitionCreate(
+ testDialect, strref("my_type"), successTypeVerify, NULL);
+ mlirDynamicDialectRegisterType(testDialect, typeDef);
+
+ // Look it up to verify registration worked.
+ MlirDialect dialect = mlirDynamicDialectGetDialect(testDialect);
+ MlirDynamicTypeDefinition lookedUp =
+ mlirExtensibleDialectLookupTypeDefinition(dialect, strref("my_type"));
+
+ // CHECK: type lookup succeeded: 1
+ fprintf(stderr, "type lookup succeeded: %d\n", lookedUp.ptr != NULL);
+
+ // Create an instance with no parameters.
+ MlirType dynType = mlirDynamicTypeGet(lookedUp, NULL, 0);
+
+ // CHECK: is dynamic type: 1
+ fprintf(stderr, "is dynamic type: %d\n", mlirTypeIsADynamicType(dynType));
+
+ // CHECK: dynamic type num params: 0
+ fprintf(stderr, "dynamic type num params: %ld\n",
+ (long)mlirDynamicTypeGetNumParams(dynType));
+}
+
+void testDynamicAttrCreation(MlirContext ctx) {
+ MlirDynamicDialect testDialect =
+ mlirDynamicDialectCreate(ctx, strref("testattr"));
+
+ // Define and register a dynamic attribute.
+ MlirDynamicAttrDefinition attrDef = mlirDynamicAttrDefinitionCreate(
+ testDialect, strref("my_attr"), successAttrVerify, NULL);
+ mlirDynamicDialectRegisterAttr(testDialect, attrDef);
+
+ // Look it up to verify registration worked.
+ MlirDialect dialect = mlirDynamicDialectGetDialect(testDialect);
+ MlirDynamicAttrDefinition lookedUp =
+ mlirExtensibleDialectLookupAttrDefinition(dialect, strref("my_attr"));
+
+ // CHECK: attr lookup succeeded: 1
+ fprintf(stderr, "attr lookup succeeded: %d\n", lookedUp.ptr != NULL);
+
+ // Create an instance with no parameters.
+ MlirAttribute dynAttr = mlirDynamicAttrGet(lookedUp, NULL, 0);
+
+ // CHECK: is dynamic attr: 1
+ fprintf(stderr, "is dynamic attr: %d\n",
+ mlirAttributeIsADynamicAttr(dynAttr));
+
+ // CHECK: dynamic attr num params: 0
+ fprintf(stderr, "dynamic attr num params: %ld\n",
+ (long)mlirDynamicAttrGetNumParams(dynAttr));
+}
+
+void testDynamicTypeWithParams(MlirContext ctx) {
+ MlirDynamicDialect testDialect =
+ mlirDynamicDialectCreate(ctx, strref("testparams"));
+
+ // Define and register a parameterized type.
+ MlirDynamicTypeDefinition typeDef = mlirDynamicTypeDefinitionCreate(
+ testDialect, strref("pair"), successTypeVerify, NULL);
+ mlirDynamicDialectRegisterType(testDialect, typeDef);
+
+ MlirDialect dialect = mlirDynamicDialectGetDialect(testDialect);
+ MlirDynamicTypeDefinition lookedUp =
+ mlirExtensibleDialectLookupTypeDefinition(dialect, strref("pair"));
+
+ // Create an instance with two integer attribute parameters.
+ MlirAttribute params[2];
+ params[0] = mlirIntegerAttrGet(mlirIntegerTypeGet(ctx, 32), 42);
+ params[1] = mlirIntegerAttrGet(mlirIntegerTypeGet(ctx, 32), 7);
+ MlirType dynType = mlirDynamicTypeGet(lookedUp, params, 2);
+
+ // CHECK: parameterized type num params: 2
+ fprintf(stderr, "parameterized type num params: %ld\n",
+ (long)mlirDynamicTypeGetNumParams(dynType));
+}
+
+int main(void) {
+ MlirContext ctx = mlirContextCreate();
+ mlirContextSetAllowUnregisteredDialects(ctx, true);
+
+ testDynamicDialectCreation(ctx);
+ testDynamicOpCreation(ctx);
+ testDynamicTypeCreation(ctx);
+ testDynamicAttrCreation(ctx);
+ testDynamicTypeWithParams(ctx);
+
+ mlirContextDestroy(ctx);
+ return 0;
+}
diff --git a/mlir/test/CMakeLists.txt b/mlir/test/CMakeLists.txt
index e0c32cd4bd9a0..d24fb79753783 100644
--- a/mlir/test/CMakeLists.txt
+++ b/mlir/test/CMakeLists.txt
@@ -102,6 +102,7 @@ configure_lit_site_cfg(
)
set(MLIR_TEST_DEPENDS
+ mlir-capi-extensible-dialect-test
mlir-capi-ir-test
mlir-capi-irdl-test
mlir-capi-llvm-test
>From 07f7e2f1d3916de21ad63f22e94b9850a2da7ef7 Mon Sep 17 00:00:00 2001
From: Edgar Luque <git at edgl.dev>
Date: Thu, 19 Mar 2026 20:15:24 +0100
Subject: [PATCH 2/2] [mlir][CAPI] Address review: use callback structs for
dynamic definitions
Rename mlirDynamicDialectGetDialect to mlirDynamicDialectAsDialect.
Replace individual function pointer parameters with callback structs
(MlirDynamicOpDefinitionCallbacks, MlirDynamicTypeDefinitionCallbacks,
MlirDynamicAttrDefinitionCallbacks) following the established MLIR C API
convention used by MlirExternalPassCallbacks and others.
---
mlir/include/mlir-c/ExtensibleDialect.h | 63 ++++++++++++++++---------
mlir/lib/CAPI/IR/ExtensibleDialect.cpp | 37 +++++++--------
mlir/test/CAPI/extensible_dialect.c | 25 ++++++----
3 files changed, 76 insertions(+), 49 deletions(-)
diff --git a/mlir/include/mlir-c/ExtensibleDialect.h b/mlir/include/mlir-c/ExtensibleDialect.h
index c0770a8920981..4a8733407faba 100644
--- a/mlir/include/mlir-c/ExtensibleDialect.h
+++ b/mlir/include/mlir-c/ExtensibleDialect.h
@@ -52,28 +52,35 @@ mlirDynamicDialectCreate(MlirContext ctx, MlirStringRef name);
/// Get the underlying MlirDialect from a MlirDynamicDialect.
MLIR_CAPI_EXPORTED MlirDialect
-mlirDynamicDialectGetDialect(MlirDynamicDialect dialect);
+mlirDynamicDialectAsDialect(MlirDynamicDialect dialect);
//===----------------------------------------------------------------------===//
/// Dynamic op definition creation
//===----------------------------------------------------------------------===//
-/// Callback to verify a dynamic op.
-typedef MlirLogicalResult (*MlirDynamicOpVerifyFn)(MlirOperation op,
- void *userData);
+/// Callbacks for a dynamic op definition. All fields may be NULL, in which
+/// case the corresponding action is a no-op / always succeeds.
+typedef struct {
+ /// Optional constructor for the user data.
+ void (*construct)(void *userData);
+ /// Optional destructor for the user data.
+ void (*destruct)(void *userData);
+ /// The callback function to verify the operation.
+ MlirLogicalResult (*verify)(MlirOperation op, void *userData);
+ /// The callback function to verify the operation with access to regions.
+ MlirLogicalResult (*verifyRegion)(MlirOperation op, void *userData);
+} MlirDynamicOpDefinitionCallbacks;
-/// Create a dynamic op definition with the given name and verifier callbacks.
+/// Create a dynamic op definition with the given name and callbacks.
/// The name should be the bare op name (e.g. "constant"), not the
/// dialect-qualified name. The definition is NOT yet registered with the
/// dialect; call mlirDynamicDialectRegisterOp to register it.
-/// Both \p verifyFn and \p verifyRegionFn may be NULL, in which case
-/// verification always succeeds. \p userData is shared between both callbacks.
+/// \p userData is shared between all callbacks.
/// If the definition is not registered, it must be destroyed with
/// mlirDynamicOpDefinitionDestroy to avoid a memory leak.
MLIR_CAPI_EXPORTED MlirDynamicOpDefinition mlirDynamicOpDefinitionCreate(
MlirDynamicDialect dialect, MlirStringRef name,
- MlirDynamicOpVerifyFn verifyFn, MlirDynamicOpVerifyFn verifyRegionFn,
- void *userData);
+ MlirDynamicOpDefinitionCallbacks callbacks, void *userData);
/// Destroy a dynamic op definition that was not registered with a dialect.
MLIR_CAPI_EXPORTED void
@@ -91,22 +98,29 @@ mlirDynamicDialectRegisterOp(MlirDynamicDialect dialect,
/// Dynamic type definition creation
//===----------------------------------------------------------------------===//
-/// Callback to verify a dynamic type's parameters.
-typedef MlirLogicalResult (*MlirDynamicTypeVerifyFn)(
- intptr_t nParams, MlirAttribute const *params, void *userData);
+/// Callbacks for a dynamic type definition. All fields may be NULL, in which
+/// case the corresponding action is a no-op / always succeeds.
+typedef struct {
+ /// Optional constructor for the user data.
+ void (*construct)(void *userData);
+ /// Optional destructor for the user data.
+ void (*destruct)(void *userData);
+ /// The callback function to verify the type's parameters.
+ MlirLogicalResult (*verify)(intptr_t nParams, MlirAttribute const *params,
+ void *userData);
+} MlirDynamicTypeDefinitionCallbacks;
-/// Create a dynamic type definition with the given name and verifier.
+/// Create a dynamic type definition with the given name and callbacks.
/// The name should be the bare type name (e.g. "mystruct"), not the
/// dialect-qualified name. The definition is NOT yet registered with the
/// dialect; call mlirDynamicDialectRegisterType to register it.
-/// \p verifyFn may be NULL, in which case verification always succeeds.
/// Note: the verifier callback does not receive diagnostic context; failures
/// are reported without a custom error message.
/// If the definition is not registered, it must be destroyed with
/// mlirDynamicTypeDefinitionDestroy to avoid a memory leak.
MLIR_CAPI_EXPORTED MlirDynamicTypeDefinition mlirDynamicTypeDefinitionCreate(
MlirDynamicDialect dialect, MlirStringRef name,
- MlirDynamicTypeVerifyFn verifyFn, void *userData);
+ MlirDynamicTypeDefinitionCallbacks callbacks, void *userData);
/// Destroy a dynamic type definition that was not registered with a dialect.
MLIR_CAPI_EXPORTED void
@@ -122,22 +136,29 @@ mlirDynamicDialectRegisterType(MlirDynamicDialect dialect,
/// Dynamic attribute definition creation
//===----------------------------------------------------------------------===//
-/// Callback to verify a dynamic attribute's parameters.
-typedef MlirLogicalResult (*MlirDynamicAttrVerifyFn)(
- intptr_t nParams, MlirAttribute const *params, void *userData);
+/// Callbacks for a dynamic attribute definition. All fields may be NULL, in
+/// which case the corresponding action is a no-op / always succeeds.
+typedef struct {
+ /// Optional constructor for the user data.
+ void (*construct)(void *userData);
+ /// Optional destructor for the user data.
+ void (*destruct)(void *userData);
+ /// The callback function to verify the attribute's parameters.
+ MlirLogicalResult (*verify)(intptr_t nParams, MlirAttribute const *params,
+ void *userData);
+} MlirDynamicAttrDefinitionCallbacks;
-/// Create a dynamic attribute definition with the given name and verifier.
+/// Create a dynamic attribute definition with the given name and callbacks.
/// The name should be the bare attr name, not the dialect-qualified name.
/// The definition is NOT yet registered with the dialect; call
/// mlirDynamicDialectRegisterAttr to register it.
-/// \p verifyFn may be NULL, in which case verification always succeeds.
/// Note: the verifier callback does not receive diagnostic context; failures
/// are reported without a custom error message.
/// If the definition is not registered, it must be destroyed with
/// mlirDynamicAttrDefinitionDestroy to avoid a memory leak.
MLIR_CAPI_EXPORTED MlirDynamicAttrDefinition mlirDynamicAttrDefinitionCreate(
MlirDynamicDialect dialect, MlirStringRef name,
- MlirDynamicAttrVerifyFn verifyFn, void *userData);
+ MlirDynamicAttrDefinitionCallbacks callbacks, void *userData);
/// Destroy a dynamic attr definition that was not registered with a dialect.
MLIR_CAPI_EXPORTED void
diff --git a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
index 2178196dbb232..960ef7c5dea19 100644
--- a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
+++ b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
@@ -33,7 +33,7 @@ MlirDynamicDialect mlirDynamicDialectCreate(MlirContext ctx,
return wrap(dialect);
}
-MlirDialect mlirDynamicDialectGetDialect(MlirDynamicDialect dialect) {
+MlirDialect mlirDynamicDialectAsDialect(MlirDynamicDialect dialect) {
return wrap(static_cast<Dialect *>(unwrap(dialect)));
}
@@ -43,19 +43,18 @@ MlirDialect mlirDynamicDialectGetDialect(MlirDynamicDialect dialect) {
MlirDynamicOpDefinition
mlirDynamicOpDefinitionCreate(MlirDynamicDialect dialect, MlirStringRef name,
- MlirDynamicOpVerifyFn verifyFn,
- MlirDynamicOpVerifyFn verifyRegionFn,
+ MlirDynamicOpDefinitionCallbacks callbacks,
void *userData) {
- auto cppVerifyFn = [verifyFn, userData](Operation *op) -> LogicalResult {
- if (!verifyFn)
+ auto cppVerifyFn = [callbacks, userData](Operation *op) -> LogicalResult {
+ if (!callbacks.verify)
return success();
- return unwrap(verifyFn(wrap(op), userData));
+ return unwrap(callbacks.verify(wrap(op), userData));
};
- auto cppVerifyRegionFn = [verifyRegionFn,
+ auto cppVerifyRegionFn = [callbacks,
userData](Operation *op) -> LogicalResult {
- if (!verifyRegionFn)
+ if (!callbacks.verifyRegion)
return success();
- return unwrap(verifyRegionFn(wrap(op), userData));
+ return unwrap(callbacks.verifyRegion(wrap(op), userData));
};
std::unique_ptr<DynamicOpDefinition> opDef = DynamicOpDefinition::get(
unwrap(name), unwrap(dialect), std::move(cppVerifyFn),
@@ -79,19 +78,19 @@ void mlirDynamicDialectRegisterOp(MlirDynamicDialect dialect,
MlirDynamicTypeDefinition
mlirDynamicTypeDefinitionCreate(MlirDynamicDialect dialect, MlirStringRef name,
- MlirDynamicTypeVerifyFn verifyFn,
+ MlirDynamicTypeDefinitionCallbacks callbacks,
void *userData) {
- auto cppVerifyFn = [verifyFn,
+ auto cppVerifyFn = [callbacks,
userData](function_ref<InFlightDiagnostic()> emitError,
ArrayRef<Attribute> params) -> LogicalResult {
- if (!verifyFn)
+ if (!callbacks.verify)
return success();
SmallVector<MlirAttribute> cParams;
cParams.reserve(params.size());
for (Attribute p : params)
cParams.push_back(wrap(p));
- return unwrap(verifyFn(static_cast<intptr_t>(cParams.size()),
- cParams.data(), userData));
+ return unwrap(callbacks.verify(static_cast<intptr_t>(cParams.size()),
+ cParams.data(), userData));
};
std::unique_ptr<DynamicTypeDefinition> typeDef = DynamicTypeDefinition::get(
unwrap(name), unwrap(dialect), std::move(cppVerifyFn));
@@ -114,19 +113,19 @@ void mlirDynamicDialectRegisterType(MlirDynamicDialect dialect,
MlirDynamicAttrDefinition
mlirDynamicAttrDefinitionCreate(MlirDynamicDialect dialect, MlirStringRef name,
- MlirDynamicAttrVerifyFn verifyFn,
+ MlirDynamicAttrDefinitionCallbacks callbacks,
void *userData) {
- auto cppVerifyFn = [verifyFn,
+ auto cppVerifyFn = [callbacks,
userData](function_ref<InFlightDiagnostic()> emitError,
ArrayRef<Attribute> params) -> LogicalResult {
- if (!verifyFn)
+ if (!callbacks.verify)
return success();
SmallVector<MlirAttribute> cParams;
cParams.reserve(params.size());
for (Attribute p : params)
cParams.push_back(wrap(p));
- return unwrap(verifyFn(static_cast<intptr_t>(cParams.size()),
- cParams.data(), userData));
+ return unwrap(callbacks.verify(static_cast<intptr_t>(cParams.size()),
+ cParams.data(), userData));
};
std::unique_ptr<DynamicAttrDefinition> attrDef = DynamicAttrDefinition::get(
unwrap(name), unwrap(dialect), std::move(cppVerifyFn));
diff --git a/mlir/test/CAPI/extensible_dialect.c b/mlir/test/CAPI/extensible_dialect.c
index cf1706f10ba84..5734045f6366a 100644
--- a/mlir/test/CAPI/extensible_dialect.c
+++ b/mlir/test/CAPI/extensible_dialect.c
@@ -45,7 +45,7 @@ void testDynamicDialectCreation(MlirContext ctx) {
// Create a dynamic dialect.
MlirDynamicDialect testDialect =
mlirDynamicDialectCreate(ctx, strref("test_dyn"));
- MlirDialect dialect = mlirDynamicDialectGetDialect(testDialect);
+ MlirDialect dialect = mlirDynamicDialectAsDialect(testDialect);
// CHECK: test_dyn is extensible: 1
fprintf(stderr, "test_dyn is extensible: %d\n",
@@ -56,9 +56,10 @@ void testDynamicOpCreation(MlirContext ctx) {
MlirDynamicDialect testDialect =
mlirDynamicDialectCreate(ctx, strref("testop"));
- // Define and register a simple op with NULL verifiers.
+ // Define and register a simple op with no callbacks.
+ MlirDynamicOpDefinitionCallbacks opCallbacks = {NULL, NULL, NULL, NULL};
MlirDynamicOpDefinition opDef = mlirDynamicOpDefinitionCreate(
- testDialect, strref("my_op"), NULL, NULL, NULL);
+ testDialect, strref("my_op"), opCallbacks, NULL);
mlirDynamicDialectRegisterOp(testDialect, opDef);
// Parse and dump IR that uses this op.
@@ -81,12 +82,14 @@ void testDynamicTypeCreation(MlirContext ctx) {
mlirDynamicDialectCreate(ctx, strref("testtype"));
// Define and register a dynamic type.
+ MlirDynamicTypeDefinitionCallbacks typeCallbacks = {NULL, NULL,
+ successTypeVerify};
MlirDynamicTypeDefinition typeDef = mlirDynamicTypeDefinitionCreate(
- testDialect, strref("my_type"), successTypeVerify, NULL);
+ testDialect, strref("my_type"), typeCallbacks, NULL);
mlirDynamicDialectRegisterType(testDialect, typeDef);
// Look it up to verify registration worked.
- MlirDialect dialect = mlirDynamicDialectGetDialect(testDialect);
+ MlirDialect dialect = mlirDynamicDialectAsDialect(testDialect);
MlirDynamicTypeDefinition lookedUp =
mlirExtensibleDialectLookupTypeDefinition(dialect, strref("my_type"));
@@ -109,12 +112,14 @@ void testDynamicAttrCreation(MlirContext ctx) {
mlirDynamicDialectCreate(ctx, strref("testattr"));
// Define and register a dynamic attribute.
+ MlirDynamicAttrDefinitionCallbacks attrCallbacks = {NULL, NULL,
+ successAttrVerify};
MlirDynamicAttrDefinition attrDef = mlirDynamicAttrDefinitionCreate(
- testDialect, strref("my_attr"), successAttrVerify, NULL);
+ testDialect, strref("my_attr"), attrCallbacks, NULL);
mlirDynamicDialectRegisterAttr(testDialect, attrDef);
// Look it up to verify registration worked.
- MlirDialect dialect = mlirDynamicDialectGetDialect(testDialect);
+ MlirDialect dialect = mlirDynamicDialectAsDialect(testDialect);
MlirDynamicAttrDefinition lookedUp =
mlirExtensibleDialectLookupAttrDefinition(dialect, strref("my_attr"));
@@ -138,11 +143,13 @@ void testDynamicTypeWithParams(MlirContext ctx) {
mlirDynamicDialectCreate(ctx, strref("testparams"));
// Define and register a parameterized type.
+ MlirDynamicTypeDefinitionCallbacks typeCallbacks = {NULL, NULL,
+ successTypeVerify};
MlirDynamicTypeDefinition typeDef = mlirDynamicTypeDefinitionCreate(
- testDialect, strref("pair"), successTypeVerify, NULL);
+ testDialect, strref("pair"), typeCallbacks, NULL);
mlirDynamicDialectRegisterType(testDialect, typeDef);
- MlirDialect dialect = mlirDynamicDialectGetDialect(testDialect);
+ MlirDialect dialect = mlirDynamicDialectAsDialect(testDialect);
MlirDynamicTypeDefinition lookedUp =
mlirExtensibleDialectLookupTypeDefinition(dialect, strref("pair"));
More information about the Mlir-commits
mailing list