Skip to content

Commit ecd06db

Browse files
ftynsekiranchandramohan
authored andcommitted
[mlir] separable registration of operation interfaces
This is similar to attribute and type interfaces and mostly the same mechanism (FallbackModel / ExternalModel, ODS generation). There are minor differences in how the concept-based polymorphism is implemented for operations that are accounted for by ODS backends, and this essentially adds a test and exposes the API. Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D104294
1 parent 2e4d158 commit ecd06db

File tree

6 files changed

+123
-11
lines changed

6 files changed

+123
-11
lines changed

mlir/docs/Interfaces.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,12 @@ if (ExampleOpInterface example = dyn_cast<ExampleOpInterface>(op))
207207
llvm::errs() << "hook returned = " << example.exampleInterfaceHook() << "\n";
208208
```
209209
210-
#### External Models for Attribute/Type Interfaces
210+
#### External Models for Attribute, Operation and Type Interfaces
211211
212-
It may be desirable to provide an interface implementation for an attribute or a
213-
type without modifying the definition of said attribute or type. Notably, this
214-
allows to implement interfaces for attributes and types outside of the dialect
215-
that defines them and, in particular, provide interfaces for built-in types.
212+
It may be desirable to provide an interface implementation for an IR object
213+
without modifying the definition of said object. Notably, this allows to
214+
implement interfaces for attributes, operations and types outside of the dialect
215+
that defines them, for example, to provide interfaces for built-in types.
216216
217217
This is achieved by extending the concept-based polymorphism model with two more
218218
classes derived from `Concept` as follows.
@@ -261,9 +261,9 @@ struct ExampleTypeInterfaceTraits {
261261
};
262262
```
263263

264-
External models can be provided for attribute and type interfaces by deriving
265-
either `FallbackModel` or `ExternalModel` and by registering the model class
266-
with the attribute or type class in a given context. Other contexts will not see
264+
External models can be provided for attribute, operation and type interfaces by
265+
deriving either `FallbackModel` or `ExternalModel` and by registering the model
266+
class with the relevant class in a given context. Other contexts will not see
267267
the interface unless registered.
268268

269269
```c++

mlir/include/mlir/IR/OpDefinition.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,6 +1621,19 @@ class Op : public OpState, public Traits<ConcreteType>... {
16211621
reinterpret_cast<Operation *>(const_cast<void *>(pointer)));
16221622
}
16231623

1624+
/// Attach the given models as implementations of the corresponding interfaces
1625+
/// for the concrete operation.
1626+
template <typename... Models>
1627+
static void attachInterface(MLIRContext &context) {
1628+
AbstractOperation *abstract = AbstractOperation::lookupMutable(
1629+
ConcreteType::getOperationName(), &context);
1630+
if (!abstract)
1631+
llvm::report_fatal_error(
1632+
"Attempting to attach an interface to an unregistered operation " +
1633+
ConcreteType::getOperationName() + ".");
1634+
abstract->interfaceMap.insert<Models...>();
1635+
}
1636+
16241637
private:
16251638
/// Trait to check if T provides a 'fold' method for a single result op.
16261639
template <typename T, typename... Args>

mlir/include/mlir/IR/OperationSupport.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ class AbstractOperation {
159159
/// Look up the specified operation in the specified MLIRContext and return a
160160
/// pointer to it if present. Otherwise, return a null pointer.
161161
static const AbstractOperation *lookup(StringRef opName,
162-
MLIRContext *context);
162+
MLIRContext *context) {
163+
return lookupMutable(opName, context);
164+
}
163165

164166
/// This constructor is used by Dialect objects when they register the list of
165167
/// operations they contain.
@@ -190,6 +192,15 @@ class AbstractOperation {
190192
GetCanonicalizationPatternsFn getCanonicalizationPatterns,
191193
detail::InterfaceMap &&interfaceMap, HasTraitFn hasTrait);
192194

195+
/// Give Op access to lookupMutable.
196+
template <typename ConcreteType, template <typename T> class... Traits>
197+
friend class Op;
198+
199+
/// Look up the specified operation in the specified MLIRContext and return a
200+
/// pointer to it if present. Otherwise, return a null pointer.
201+
static AbstractOperation *lookupMutable(StringRef opName,
202+
MLIRContext *context);
203+
193204
/// A map of interfaces that were registered to this operation.
194205
detail::InterfaceMap interfaceMap;
195206

mlir/lib/IR/MLIRContext.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -693,8 +693,8 @@ ParseResult AbstractOperation::parseAssembly(OpAsmParser &parser,
693693

694694
/// Look up the specified operation in the operation set and return a pointer
695695
/// to it if present. Otherwise, return a null pointer.
696-
const AbstractOperation *AbstractOperation::lookup(StringRef opName,
697-
MLIRContext *context) {
696+
AbstractOperation *AbstractOperation::lookupMutable(StringRef opName,
697+
MLIRContext *context) {
698698
auto &impl = context->getImpl();
699699
auto it = impl.registeredOperations.find(opName);
700700
if (it != impl.registeredOperations.end())

mlir/test/lib/Dialect/Test/TestInterfaces.td

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,24 @@ def TestExternalAttrInterface : AttrInterface<"TestExternalAttrInterface"> {
8888
];
8989
}
9090

91+
def TestExternalOpInterface : OpInterface<"TestExternalOpInterface"> {
92+
let cppNamespace = "::mlir";
93+
let methods = [
94+
InterfaceMethod<"Returns the length of the operation name plus arg.",
95+
"unsigned", "getNameLengthPlusArg", (ins "unsigned":$arg)>,
96+
StaticInterfaceMethod<
97+
"Returns the length of the operation name plus arg twice.", "unsigned",
98+
"getNameLengthPlusArgTwice", (ins "unsigned":$arg)>,
99+
InterfaceMethod<
100+
"Returns the length of the product of the operation name and arg.",
101+
"unsigned", "getNameLengthTimesArg", (ins "unsigned":$arg), "",
102+
"return arg * $_op->getName().getStringRef().size();">,
103+
StaticInterfaceMethod<"Returns the length of the operation name minus arg.",
104+
"unsigned", "getNameLengthMinusArg", (ins "unsigned":$arg), "",
105+
"return ConcreteOp::getOperationName().size() - arg;">,
106+
];
107+
}
108+
91109
def TestEffectOpInterface
92110
: EffectOpInterfaceBase<"TestEffectOpInterface",
93111
"::mlir::TestEffects::Effect"> {

mlir/unittests/IR/InterfaceAttachmentTest.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "mlir/IR/BuiltinAttributes.h"
15+
#include "mlir/IR/BuiltinOps.h"
1516
#include "mlir/IR/BuiltinTypes.h"
1617
#include "gtest/gtest.h"
1718

1819
#include "../../test/lib/Dialect/Test/TestAttributes.h"
20+
#include "../../test/lib/Dialect/Test/TestDialect.h"
1921
#include "../../test/lib/Dialect/Test/TestTypes.h"
2022

2123
using namespace mlir;
@@ -150,4 +152,72 @@ TEST(InterfaceAttachment, Attribute) {
150152
EXPECT_EQ(iface.getSomeNumber(), 42);
151153
}
152154

155+
/// External interface model for the module operation. Only provides non-default
156+
/// methods.
157+
struct TestExternalOpModel
158+
: public TestExternalOpInterface::ExternalModel<TestExternalOpModel,
159+
ModuleOp> {
160+
unsigned getNameLengthPlusArg(Operation *op, unsigned arg) const {
161+
return op->getName().getStringRef().size() + arg;
162+
}
163+
164+
static unsigned getNameLengthPlusArgTwice(unsigned arg) {
165+
return ModuleOp::getOperationName().size() + 2 * arg;
166+
}
167+
};
168+
169+
/// External interface model for the func operation. Provides non-deafult and
170+
/// overrides default methods.
171+
struct TestExternalOpOverridingModel
172+
: public TestExternalOpInterface::FallbackModel<
173+
TestExternalOpOverridingModel> {
174+
unsigned getNameLengthPlusArg(Operation *op, unsigned arg) const {
175+
return op->getName().getStringRef().size() + arg;
176+
}
177+
178+
static unsigned getNameLengthPlusArgTwice(unsigned arg) {
179+
return FuncOp::getOperationName().size() + 2 * arg;
180+
}
181+
182+
unsigned getNameLengthTimesArg(Operation *op, unsigned arg) const {
183+
return 42;
184+
}
185+
186+
static unsigned getNameLengthMinusArg(unsigned arg) { return 21; }
187+
};
188+
189+
TEST(InterfaceAttachment, Operation) {
190+
MLIRContext context;
191+
192+
// Initially, the operation doesn't have the interface.
193+
auto moduleOp = ModuleOp::create(UnknownLoc::get(&context));
194+
ASSERT_FALSE(isa<TestExternalOpInterface>(moduleOp.getOperation()));
195+
196+
// We can attach an external interface and now the operaiton has it.
197+
ModuleOp::attachInterface<TestExternalOpModel>(context);
198+
auto iface = dyn_cast<TestExternalOpInterface>(moduleOp.getOperation());
199+
ASSERT_TRUE(iface != nullptr);
200+
EXPECT_EQ(iface.getNameLengthPlusArg(10), 16u);
201+
EXPECT_EQ(iface.getNameLengthTimesArg(3), 18u);
202+
EXPECT_EQ(iface.getNameLengthPlusArgTwice(18), 42u);
203+
EXPECT_EQ(iface.getNameLengthMinusArg(5), 1u);
204+
205+
// Default implementation can be overridden.
206+
auto funcOp = FuncOp::create(UnknownLoc::get(&context), "function",
207+
FunctionType::get(&context, {}, {}));
208+
ASSERT_FALSE(isa<TestExternalOpInterface>(funcOp.getOperation()));
209+
FuncOp::attachInterface<TestExternalOpOverridingModel>(context);
210+
iface = dyn_cast<TestExternalOpInterface>(funcOp.getOperation());
211+
ASSERT_TRUE(iface != nullptr);
212+
EXPECT_EQ(iface.getNameLengthPlusArg(10), 14u);
213+
EXPECT_EQ(iface.getNameLengthTimesArg(0), 42u);
214+
EXPECT_EQ(iface.getNameLengthPlusArgTwice(8), 20u);
215+
EXPECT_EQ(iface.getNameLengthMinusArg(1000), 21u);
216+
217+
// Another context doesn't have the interfaces registered.
218+
MLIRContext other;
219+
auto otherModuleOp = ModuleOp::create(UnknownLoc::get(&other));
220+
ASSERT_FALSE(isa<TestExternalOpInterface>(otherModuleOp.getOperation()));
221+
}
222+
153223
} // end namespace

0 commit comments

Comments
 (0)