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

Mehdi Amini llvmlistbot at llvm.org
Tue Sep 29 03:34:40 PDT 2026


https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/226323

>From 30c1bf922fc13eaeb2fd93532a64cf106f04c1b9 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Wed, 23 Sep 2026 20:26:17 -0700
Subject: [PATCH] [mlir][ODS] Copy constant generated interface model
 prototypes

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. Normal PCH compile CPU is unchanged within noise.

Assisted-by: Codex
---
 mlir/include/mlir/Support/InterfaceSupport.h | 28 +++++++++++++++++---
 mlir/test/mlir-tblgen/op-interface.td        |  5 +++-
 mlir/tools/mlir-tblgen/OpInterfacesGen.cpp   |  7 ++++-
 3 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/mlir/include/mlir/Support/InterfaceSupport.h b/mlir/include/mlir/Support/InterfaceSupport.h
index 6f654f1785713..8ab691454f64b 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,16 @@ 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 or
+  // use a custom constructor to configure callbacks.
+  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 +240,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 b156fe195a7ed..c13228fef0d16 100644
--- a/mlir/test/mlir-tblgen/op-interface.td
+++ b/mlir/test/mlir-tblgen/op-interface.td
@@ -184,9 +184,12 @@ def DeclareMethodsWithDefaultOp : Op<TestDialect, "declare_methods_op",
 // DECL-LABEL: TestOpInterfaceInterfaceTraits
 // DECL: static int shared_shared_body_foo(const Concept *impl, ::mlir::Operation *tablegen_opaque_val, int input);
 // DECL: class Model : public Concept
-// DECL: Model() : Concept{foo, body_foo, Concept::shared_shared_body_foo, mixed_body_foo, default_foo}
+// DECL: using GeneratedModel = Model;
+// DECL: constexpr Model() : Concept{foo, body_foo, Concept::shared_shared_body_foo, mixed_body_foo, default_foo}
 // DECL: static inline int foo(
 // DECL: class FallbackModel : public Concept
+// DECL-NOT: using GeneratedModel = Model;
+// DECL: FallbackModel() : Concept{foo, body_foo, Concept::shared_shared_body_foo, mixed_body_foo, default_foo}
 // DECL: static inline int foo(
 // DECL: class TestOpInterface : public ::mlir::OpInterface<TestOpInterface, detail::TestOpInterfaceInterfaceTraits>
 
diff --git a/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp b/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
index 764357720ddab..3f62969312de8 100644
--- a/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
@@ -316,11 +316,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) {
           if (StringRef(modelClass) == "Model" &&



More information about the Mlir-commits mailing list