[Mlir-commits] [mlir] feat(mlir/tblgen): forward overloaded interface methods to external models (PR #203507)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Jun 12 05:08:31 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Ege Beysel (egebeysel)

<details>
<summary>Changes</summary>

The FallbackModel/ExternalModel forwarders generated for an OpInterface dispatched to the concrete model by the method's unique name (e.g. `getOverloadedValue_1`), whereas the Model path already forwards by the method's source name. For an interface that overloads a method this prevents an external model from overriding an overload by its real name. Forward by the source name in both paths so external models can implement overloaded interface methods. No change for non-overloaded methods, where the unique and source names coincide.

Assisted-by: Claude Code

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


3 Files Affected:

- (modified) mlir/test/lib/Dialect/Test/TestInterfaces.td (+12) 
- (modified) mlir/tools/mlir-tblgen/OpInterfacesGen.cpp (+10-4) 
- (modified) mlir/unittests/IR/InterfaceAttachmentTest.cpp (+43) 


``````````diff
diff --git a/mlir/test/lib/Dialect/Test/TestInterfaces.td b/mlir/test/lib/Dialect/Test/TestInterfaces.td
index 3697e38ac4c7d..c5787d00e853e 100644
--- a/mlir/test/lib/Dialect/Test/TestInterfaces.td
+++ b/mlir/test/lib/Dialect/Test/TestInterfaces.td
@@ -91,6 +91,18 @@ def TestExternalTypeInterface : TypeInterface<"TestExternalTypeInterface"> {
     StaticInterfaceMethod<"Returns the argument.",
       "unsigned", "staticGetArgument", (ins "unsigned":$arg), "",
       "return arg;">,
+    // Overloaded method (same name, different arity) used to check that an
+    // external model can override an overloaded interface method by its source
+    // name. Both overloads have a default so other models stay unaffected.
+    InterfaceMethod<"Overloaded method, single argument.",
+      "unsigned", "getOverloadedValue", (ins "unsigned":$arg), "",
+      "return 1000 + arg;">,
+    InterfaceMethod<"Overloaded method, two arguments.",
+      "unsigned", "getOverloadedValue", (ins "unsigned":$arg, "unsigned":$arg2),
+      "", "return 2000 + arg + arg2;">,
+    InterfaceMethod<"Overloaded method, three arguments.",
+      "unsigned", "getOverloadedValue", (ins "unsigned":$arg, "unsigned":$arg2, "unsigned":$arg3),
+      "", "return 3000 + arg + arg2 + arg3;">,
   ];
 }
 
diff --git a/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp b/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
index ab8d534a99f19..8dc04c52039df 100644
--- a/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
@@ -289,6 +289,10 @@ void InterfaceGenerator::emitModelDecl(const Interface &interface) {
     os << "  class " << modelClass << " : public Concept {\n  public:\n";
     os << "    using Interface = " << interface.getFullyQualifiedName()
        << ";\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{";
     llvm::interleaveComma(
         interface.getMethods(), os,
@@ -322,7 +326,7 @@ void InterfaceGenerator::emitModelDecl(const Interface &interface) {
     if (method.isStatic())
       os << "static ";
     emitCPPType(method.getReturnType(), os);
-    os << method.getUniqueName() << "(";
+    os << method.getName() << "(";
     if (!method.isStatic()) {
       emitCPPType(valueType, os);
       os << "tablegen_opaque_val";
@@ -394,8 +398,10 @@ void InterfaceGenerator::emitModelMethodsDef(const Interface &interface) {
     else
       os << "return static_cast<const " << valueTemplate << " *>(impl)->";
 
-    // Add the arguments to the call.
-    os << method.getUniqueName() << '(';
+    // Add the arguments to the call. Forward by the (possibly non-unique)
+    // method name so that overloaded interface methods resolve to the right
+    // concrete-model overload.
+    os << method.getName() << '(';
     if (!method.isStatic())
       os << "tablegen_opaque_val" << (method.arg_empty() ? "" : ", ");
     llvm::interleaveComma(
@@ -415,7 +421,7 @@ void InterfaceGenerator::emitModelMethodsDef(const Interface &interface) {
        << "InterfaceTraits::ExternalModel<ConcreteModel, " << valueTemplate
        << ">::";
 
-    os << method.getUniqueName() << "(";
+    os << method.getName() << "(";
     if (!method.isStatic()) {
       emitCPPType(valueType, os);
       os << "tablegen_opaque_val";
diff --git a/mlir/unittests/IR/InterfaceAttachmentTest.cpp b/mlir/unittests/IR/InterfaceAttachmentTest.cpp
index e1e65dad27980..0f29244050b98 100644
--- a/mlir/unittests/IR/InterfaceAttachmentTest.cpp
+++ b/mlir/unittests/IR/InterfaceAttachmentTest.cpp
@@ -90,6 +90,49 @@ TEST(InterfaceAttachment, Type) {
   EXPECT_FALSE(isa<TestExternalTypeInterface>(i8other));
 }
 
+/// External interface model that overrides the OVERLOADED `getOverloadedValue`
+/// methods by their source name (both arities).
+struct OverloadModel
+    : public TestExternalTypeInterface::ExternalModel<OverloadModel,
+                                                      IntegerType> {
+  using Base =
+      TestExternalTypeInterface::ExternalModel<OverloadModel, IntegerType>;
+  using Base::getOverloadedValue;
+  // Required (non-default) methods of the interface.
+  unsigned getBitwidthPlusArg(Type type, unsigned arg) const {
+    return type.getIntOrFloatBitWidth() + arg;
+  }
+  static unsigned staticGetSomeValuePlusArg(unsigned arg) { return 42 + arg; }
+
+  // Overrides of the two overloads, by source name.
+  unsigned getOverloadedValue(Type type, unsigned arg) const { return arg; }
+  unsigned getOverloadedValue(Type type, unsigned arg, unsigned arg2) const {
+    return arg * arg2;
+  }
+};
+
+// An external model must be able to override an overloaded interface method by
+// its source name; the generated forwarders must dispatch there rather than
+// fall back to the inherited default.
+TEST(InterfaceAttachment, OverloadedExternalModel) {
+  MLIRContext context;
+  IntegerType i8 = IntegerType::get(&context, 8);
+  IntegerType::attachInterface<OverloadModel>(context);
+  TestExternalTypeInterface iface = dyn_cast<TestExternalTypeInterface>(i8);
+  ASSERT_TRUE(iface != nullptr);
+
+  // First overload (unique name == source name): dispatched to the override.
+  EXPECT_EQ(iface.getOverloadedValue(7u), 7u);
+
+  // Second overload: overridden by source name. If the forwarder dispatches by
+  // the mangled unique name instead, the override is missed and the inherited
+  // default (2000 + 6 + 7 == 2013) runs.
+  EXPECT_EQ(iface.getOverloadedValue(6u, 7u), 42u);
+
+  // Third overload: not overridden, dispatched to the base implementation.
+  EXPECT_EQ(iface.getOverloadedValue(6u, 7u, 8u), 3021u);
+}
+
 /// External interface model for the test type from the test dialect.
 struct TestTypeModel
     : public TestExternalTypeInterface::ExternalModel<TestTypeModel,

``````````

</details>


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


More information about the Mlir-commits mailing list