[Mlir-commits] [mlir] [mlir][ODS] Copy constant generated interface model prototypes (PR #226323)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Sep 24 15:57:01 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Mehdi Amini (joker-eph)

<details>
<summary>Changes</summary>

Generate constexpr constructors for interface models whose concept is a table of callbacks. Construct exact generated models from a static prototype; keep external and fallback models on their existing path.

In OpenMPDialect.cpp this saves about 0.22B compiler instructions and 10.5KB of text. 

Assisted-by: Codex

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


3 Files Affected:

- (modified) mlir/include/mlir/Support/InterfaceSupport.h (+24-3) 
- (modified) mlir/test/mlir-tblgen/op-interface.td (+6) 
- (modified) mlir/tools/mlir-tblgen/OpInterfacesGen.cpp (+6-1) 


``````````diff
diff --git a/mlir/include/mlir/Support/InterfaceSupport.h b/mlir/include/mlir/Support/InterfaceSupport.h
index 6f654f1785713..64cd37297a52b 100644
--- a/mlir/include/mlir/Support/InterfaceSupport.h
+++ b/mlir/include/mlir/Support/InterfaceSupport.h
@@ -18,6 +18,8 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/Support/TypeName.h"
 
+#include <type_traits>
+
 namespace mlir {
 namespace detail {
 //===----------------------------------------------------------------------===//
@@ -157,6 +159,15 @@ class InterfaceMap {
   template <typename T>
   using detect_initialize_method = llvm::is_detected<has_initialize_method, T>;
 
+  // Only exact generated models with trivial copy construction can be copied
+  // from a constant prototype. Derived models may carry additional state.
+  template <typename T, typename = void>
+  struct IsConstexprGeneratedModel : std::false_type {};
+  template <typename T>
+  struct IsConstexprGeneratedModel<T, std::void_t<typename T::GeneratedModel>>
+      : std::bool_constant<std::is_same_v<T, typename T::GeneratedModel> &&
+                           std::is_trivially_copy_constructible_v<T>> {};
+
 public:
   InterfaceMap() = default;
   InterfaceMap(InterfaceMap &&) = default;
@@ -228,9 +239,19 @@ class InterfaceMap {
     // static_assert(std::is_trivially_destructible_v<InterfaceModel>,
     //               "interface models must be trivially destructible");
 
-    // Build the interface model, optionally initializing if necessary.
-    InterfaceModel *model =
-        new (malloc(sizeof(InterfaceModel))) InterfaceModel();
+    // Generated models contain a fixed table of callbacks. Copy a constant
+    // prototype so the callbacks need not be set up for each model.
+    InterfaceModel *model;
+    if constexpr (IsConstexprGeneratedModel<InterfaceModel>::value) {
+      static constexpr InterfaceModel prototype;
+      model = new (malloc(sizeof(InterfaceModel))) InterfaceModel(prototype);
+    } else {
+      // Construct fallback, external, and custom models normally; they may
+      // need more than a copy of the generated callback table.
+      model = new (malloc(sizeof(InterfaceModel))) InterfaceModel();
+    }
+    // Run model-specific initialization after either path, including links to
+    // registered base interfaces.
     if constexpr (detect_initialize_method<InterfaceModel>::value)
       model->initializeInterfaceConcept(*this);
 
diff --git a/mlir/test/mlir-tblgen/op-interface.td b/mlir/test/mlir-tblgen/op-interface.td
index aa71baddf58cd..377082a000318 100644
--- a/mlir/test/mlir-tblgen/op-interface.td
+++ b/mlir/test/mlir-tblgen/op-interface.td
@@ -163,6 +163,12 @@ def DeclareMethodsWithDefaultOp : Op<TestDialect, "declare_methods_op",
       [DeclareOpInterfaceMethods<TestOpInterface, ["default_foo"]>]>;
 
 // DECL-LABEL: TestOpInterfaceInterfaceTraits
+// DECL: class Model : public Concept
+// DECL: using GeneratedModel = Model;
+// DECL: constexpr Model() : Concept{foo, body_foo, default_foo}
+// DECL: class FallbackModel : public Concept
+// DECL-NOT: using GeneratedModel = Model;
+// DECL: FallbackModel() : Concept{foo, body_foo, default_foo}
 // DECL: class TestOpInterface : public ::mlir::OpInterface<TestOpInterface, detail::TestOpInterfaceInterfaceTraits>
 
 // DECL: /// some function comment
diff --git a/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp b/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
index 8f4a2f3907a53..ba938c2ec5162 100644
--- a/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
@@ -289,11 +289,16 @@ void InterfaceGenerator::emitModelDecl(const Interface &interface) {
     os << "  class " << modelClass << " : public Concept {\n  public:\n";
     os << "    using Interface = " << interface.getFullyQualifiedName()
        << ";\n";
+    if (StringRef(modelClass) == "Model")
+      os << "    using GeneratedModel = Model;\n";
     // The Concept's function-pointer members and these wrapper signatures are
     // named by the unique name so that overloaded interface methods stay
     // distinct here; only the forward target (the concrete model call) uses the
     // shared source name. Do not collapse these to getName().
-    os << "    " << modelClass << "() : Concept{";
+    os << "    ";
+    if (StringRef(modelClass) == "Model")
+      os << "constexpr ";
+    os << modelClass << "() : Concept{";
     llvm::interleaveComma(
         interface.getMethods(), os,
         [&](const InterfaceMethod &method) { os << method.getUniqueName(); });

``````````

</details>


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


More information about the Mlir-commits mailing list