This mechanism makes it possible for a dialect to not register all
operations but still answer interface-based queries.
This can useful for dialects that are "open" or connected to an external
system and still interoperate with the compiler. It can also open up the
possibility to have a more extensible compiler at runtime: the compiler
does not need a pre-registration for each operation and the dialect can
inject behavior dynamically.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Unit Tests
Time | Test | |
---|---|---|
60 ms | x64 windows > LLVM.CodeGen/XCore::threads.ll |
Event Timeline
I forget, what was River's suggestion to make this more efficient?
mlir/include/mlir/IR/OpDefinition.h | ||
---|---|---|
1747 | Nit: if (auto *opIface = ...) return to reduce scope (and not used in early return) | |
mlir/test/lib/Dialect/Test/TestDialect.cpp | ||
177 | If this was return op->getName().getStringRef() == "test.unregistered_side_effect_op"; would that also restrict it to this op? | |
180 | Instantiate | |
181 | Why is ::mlir needed? | |
182 | Is there a reason why not unique_ptr? | |
184 | So for every op, one would have an entry here? Probably up to dialect to implement in efficient way for the fallback behavior they want. |
I forget, what was River's suggestion to make this more efficient?
There wasn't an efficiency aspect left, the idea River had was about generating a different base class with TableGen so that the subclass implementation for the interface fallback could be friendlier to write.
Address comments
mlir/test/lib/Dialect/Test/TestDialect.cpp | ||
---|---|---|
177 | Yes, I updated the code to make it more clear. | |
181 | Nothing, copy/pasted from somewhere else. Removed! | |
182 | It is a void*, making it a unique_ptr would require to declare some private classes. I'd expect something not in the "TestDialect" to use another more structured solution. | |
184 | Yes! |
Can you add documentation for this?
mlir/include/mlir/IR/OpDefinition.h | ||
---|---|---|
1750 | nit: Please use complete sentences in comments. | |
1751–1752 | ||
mlir/test/lib/Dialect/Test/TestDialect.cpp | ||
168 | Functions should be static and top-level. | |
182 | You only have to forward declare them. You could even put the class in the dialect declaration: // in .td let extraClassDeclaration = [{ ... private: class CustomFallbackInterface; // Storage for a custom fallback interface. std::unique_ptr<CustomFallbackInterface> fallbackEffectOpInterfaces; }]; // in .cpp class TestDialect::CustomFallbackInterface : public TestEffectOpInterface::Model<TestOpEffectInterfaceFallback> {}; | |
mlir/tools/mlir-tblgen/DialectGen.cpp | ||
147 | You need to add ::mlir:: to TypeID and Operation to not break out-of-tree projects. |
Address River's comment
mlir/test/lib/Dialect/Test/TestDialect.cpp | ||
---|---|---|
182 | It won't work because the constructor body is in the header... |
mlir/docs/Interfaces.md | ||
---|---|---|
237 ↗ | (On Diff #332061) | Do we need to pass in the operation here? Or can we just pass in the OperationName instead? We have several few places within the code base that may try to use the AbstractOperation to check for interfaces without having a concrete operation instead, it would be great not to lose that ability. |
mlir/include/mlir/IR/OpDefinition.h | ||
1755 | Drop the getIdentifier here? |
Use an Identifier instead of an Operation* to retrieve the interface
mlir/docs/Interfaces.md | ||
---|---|---|
237 ↗ | (On Diff #332061) | Yes the name should be enough here. The original version of the patched needed the operation here because it was captured in the returned instance IIRC. |
mlir/docs/Interfaces.md | ||
---|---|---|
237 ↗ | (On Diff #332061) | Why Identifier and not OperationName? |
Nice feature! I have one small question. Can this fallback OpInterface be represented as DialectInterface itself? In that case it can be registered in the Dialect via addInterfaces method without manual memory management (destructor overload or unique_ptr).
struct TestOpEffectInterfaceFallback : public DialectInterface::Base<TestEffectOpInterface::FallbackModel<TestOpEffectInterfaceFallback>> { ... }; void TestDialect::initialize() { addInterfaces<TestOpEffectInterfaceFallback>(); } void *TestDialect::getRegisteredInterfaceForOp(TypeID typeID, OperationName opName) { if (opName.getIdentifier() == "test.unregistered_side_effect_op" && typeID == TypeID::get<TestEffectOpInterface>()) return getRegisteredInterface<TestOpEffectInterfaceFallback>(); return nullptr; }
That's an excellent remark!
I intentionally did it this way because in my original mock-up I wanted to be able to initialize the interface instance with some state and keep it around. But it may not be warranted actually.
Nit:
if (auto *opIface = ...) return
to reduce scope (and not used in early return)