Skip to content

Commit 41f00cb

Browse files
authored
[MLIR] feat(mlir-tblgen): Add support for dialect interfaces (#170046)
Currently, Dialect Interfaces can't be defined in ODS. This PR adds the support for dialect interfaces. It follows the same approach with other interfaces and extends on top of `Interface` class defined in `mlir/TableGen/Interfaces.h`. Given the following input: ```tablegen #ifndef MY_INTERFACES #define MY_INTERFACES include "mlir/IR/Interfaces.td" def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> { let description = [{ Define a base inlining interface class to allow for dialects to opt-in to the inliner. }]; let cppNamespace = "::mlir"; let methods = [ InterfaceMethod< /*desc=*/ [{ Returns true if the given region 'src' can be inlined into the region 'dest' that is attached to an operation registered to the current dialect. 'valueMapping' contains any remapped values from within the 'src' region. This can be used to examine what values will replace entry arguments into the 'src' region, for example. }], /*returnType=*/ "bool", /*methodName=*/ "isLegalToInline", /*args=*/ (ins "::mlir::Region *":$dest, "::mlir::Region *":$src, "::mlir::IRMapping &":$valueMapping), /*methodBody=*/ [{ return true; }] > ]; } #endif ``` It will generate the following code: ```cpp /*===- TableGen'erated file -------------------------------------*- C++ -*-===*\ |* *| |* Dialect Interface Declarations *| |* *| |* Automatically generated file, do not edit! *| |* *| \*===----------------------------------------------------------------------===*/ namespace mlir { /// Define a base inlining interface class to allow for dialects to opt-in to the inliner. class DialectInlinerInterface : public ::mlir::DialectInterface::Base<DialectInlinerInterface> { public: /// Returns true if the given region 'src' can be inlined into the region /// 'dest' that is attached to an operation registered to the current dialect. /// 'valueMapping' contains any remapped values from within the 'src' region. /// This can be used to examine what values will replace entry arguments into /// the 'src' region, for example. virtual bool isLegalToInline(::mlir::Region * dest, ::mlir::Region * src, ::mlir::IRMapping & valueMapping) const; protected: DialectInlinerInterface(::mlir::Dialect *dialect) : Base(dialect) {} }; } // namespace mlir bool ::mlir::DialectInlinerInterface::isLegalToInline(::mlir::Region * dest, ::mlir::Region * src, ::mlir::IRMapping & valueMapping) const { return true; } ```
1 parent 7f2e6f1 commit 41f00cb

File tree

11 files changed

+520
-156
lines changed

11 files changed

+520
-156
lines changed

mlir/docs/Interfaces.md

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,72 @@ if (DialectInlinerInterface *interface = dyn_cast<DialectInlinerInterface>(diale
8585
}
8686
```
8787

88+
#### Utilizing the ODS framework
89+
90+
Note: Before reading this section, the reader should have some familiarity with
91+
the concepts described in the
92+
[`Operation Definition Specification`](DefiningDialects/Operations.md) documentation.
93+
94+
MLIR also supports defining dialect interfaces directly in **TableGen**.
95+
This reduces boilerplate and allows authors to specify high-level interface
96+
structure declaratively.
97+
98+
For example, the above interface can be defined using ODS as follows:
99+
100+
```tablegen
101+
def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> {
102+
let description = [{
103+
Define a base inlining interface class to allow for dialects to opt-in to
104+
the inliner.
105+
}];
106+
107+
let methods = [
108+
InterfaceMethod<[{
109+
Returns true if the given region 'src' can be inlined into the region
110+
'dest' that is attached to an operation registered to the current dialect.
111+
'valueMapping' contains any remapped values from within the 'src' region.
112+
This can be used to examine what values will replace entry arguments into
113+
the 'src' region, for example.
114+
}],
115+
"bool", "isLegalToInline",
116+
(ins "Region *":$dest, "Region *":$src, "IRMapping &":$valueMapping),
117+
[{
118+
return false;
119+
}]
120+
>
121+
];
122+
}
123+
```
124+
125+
`DialectInterfaces` class make use of the following components:
126+
127+
* C++ Class Name (Provided via template parameter)
128+
- The name of the C++ interface class.
129+
* Description (`description`)
130+
- A string description of the interface, its invariants, example usages,
131+
etc.
132+
* C++ Namespace (`cppNamespace`)
133+
- The C++ namespace that the interface class should be generated in.
134+
* Methods (`methods`)
135+
- The list of interface hook methods that are defined by the IR object.
136+
- The structure of these methods is defined [here](#interface-methods).
137+
138+
The header file can be generated via the following command:
139+
140+
```bash
141+
mlir-tblgen --gen-dialect-interface-decls DialectInterface.td
142+
```
143+
144+
To generate dialect interface declarations using the ODS framework in CMake, you would write:
145+
146+
```cmake
147+
set(LLVM_TARGET_DEFINITIONS DialectInlinerInterface.td)
148+
mlir_tablegen(DialectInlinerInterface.h.inc -gen-dialect-interface-decls)
149+
```
150+
151+
An example of this can be found in the DialectInlinerInterface implementation
152+
and the related `CMakeLists.txt` under `mlir/include/mlir/Transforms`.
153+
88154
#### DialectInterfaceCollection
89155

90156
An additional utility is provided via `DialectInterfaceCollection`. This class
@@ -364,10 +430,6 @@ void *TestDialect::getRegisteredInterfaceForOp(TypeID typeID,
364430
365431
#### Utilizing the ODS Framework
366432
367-
Note: Before reading this section, the reader should have some familiarity with
368-
the concepts described in the
369-
[`Operation Definition Specification`](DefiningDialects/Operations.md) documentation.
370-
371433
As detailed above, [Interfaces](#attributeoperationtype-interfaces) allow for
372434
attributes, operations, and types to expose method calls without requiring that
373435
the caller know the specific derived type. The downside to this infrastructure,

mlir/include/mlir/IR/Interfaces.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ class TypeInterface<string name, list<Interface> baseInterfaces = []>
147147
!if(!empty(cppNamespace),"", cppNamespace # "::") # name
148148
>;
149149

150+
// DialectInterface represents a Dialect Interface.
151+
class DialectInterface<string name, list<Interface> baseInterfaces = []>
152+
: Interface<name, baseInterfaces>, OpInterfaceTrait<name>;
153+
154+
150155
// Whether to declare the interface methods in the user entity's header. This
151156
// class simply wraps an Interface but is used to indicate that the method
152157
// declarations should be generated. This class takes an optional set of methods

mlir/include/mlir/TableGen/Interfaces.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ struct TypeInterface : public Interface {
157157

158158
static bool classof(const Interface *interface);
159159
};
160+
// An interface that is registered to a Dialect.
161+
struct DialectInterface : public Interface {
162+
using Interface::Interface;
163+
164+
static bool classof(const Interface *interface);
165+
};
166+
160167
} // namespace tblgen
161168
} // namespace mlir
162169

mlir/include/mlir/Transforms/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,8 @@ mlir_tablegen(Transforms.capi.h.inc -gen-pass-capi-header --prefix Transforms)
55
mlir_tablegen(Transforms.capi.cpp.inc -gen-pass-capi-impl --prefix Transforms)
66
add_mlir_dialect_tablegen_target(MLIRTransformsPassIncGen)
77

8+
set(LLVM_TARGET_DEFINITIONS DialectInlinerInterface.td)
9+
mlir_tablegen(DialectInlinerInterface.h.inc -gen-dialect-interface-decls)
10+
add_mlir_dialect_tablegen_target(MLIRTransformsDialectInterfaceIncGen)
11+
812
add_mlir_doc(Passes GeneralPasses ./ -gen-pass-doc)
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
#ifndef MLIR_INTERFACES_DIALECTINLINERINTERFACE
2+
#define MLIR_INTERFACES_DIALECTINLINERINTERFACE
3+
4+
include "mlir/IR/Interfaces.td"
5+
6+
def DialectInlinerInterface : DialectInterface<"DialectInlinerInterface"> {
7+
let description = [{
8+
This is the interface that must be implemented by the dialects of operations
9+
to be inlined. This interface should only handle the operations of the
10+
given dialect.
11+
}];
12+
let cppNamespace = "::mlir";
13+
14+
let methods = [
15+
InterfaceMethod<[{
16+
Returns true if the given operation 'callable', that implements the
17+
'CallableOpInterface', can be inlined into the position given call
18+
operation 'call', that is registered to the current dialect and implements
19+
the `CallOpInterface`. 'wouldBeCloned' is set to true if the region of the
20+
given 'callable' is set to be cloned during the inlining process, or false
21+
if the region is set to be moved in-place(i.e. no duplicates would be
22+
created).
23+
}],
24+
"bool", "isLegalToInline",
25+
(ins "::mlir::Operation *":$call, "::mlir::Operation *":$callable,
26+
"bool":$wouldBeCloned),
27+
[{
28+
return false;
29+
}]
30+
>,
31+
InterfaceMethod<[{
32+
Returns true if the given region 'src' can be inlined into the region
33+
'dest' that is attached to an operation registered to the current dialect.
34+
'wouldBeCloned' is set to true if the given 'src' region is set to be
35+
cloned during the inlining process, or false if the region is set to be
36+
moved in-place (i.e. no duplicates would be created). 'valueMapping'
37+
contains any remapped values from within the 'src' region. This can be
38+
used to examine what values will replace entry arguments into the 'src'
39+
region for example.
40+
}],
41+
"bool", "isLegalToInline",
42+
(ins "::mlir::Region *":$dest, "::mlir::Region *":$src, "bool":$wouldBeCloned,
43+
"::mlir::IRMapping &":$valueMapping),
44+
[{
45+
return false;
46+
}]
47+
>,
48+
InterfaceMethod<[{
49+
Returns true if the given region 'src' can be inlined into the region
50+
'dest' that is attached to an operation registered to the current dialect.
51+
'wouldBeCloned' is set to true if the given 'src' region is set to be
52+
cloned during the inlining process, or false if the region is set to be
53+
moved in-place(i.e. no duplicates would be created). 'valueMapping'
54+
contains any remapped values from within the 'src' region. This can be
55+
used to examine what values will replace entry arguments into the 'src'
56+
region for example.
57+
}],
58+
"bool", "isLegalToInline",
59+
(ins "::mlir::Operation *":$op, "::mlir::Region *":$dest,
60+
"bool":$wouldBeCloned, "::mlir::IRMapping &":$valueMapping),
61+
[{
62+
return false;
63+
}]
64+
>,
65+
InterfaceMethod<[{
66+
This hook is invoked on an operation that contains regions. It should
67+
return true if the analyzer should recurse within the regions of this
68+
operation when computing legality and cost, false otherwise. The default
69+
implementation returns true.
70+
}],
71+
"bool", "shouldAnalyzeRecursively",
72+
(ins "::mlir::Operation *":$op),
73+
[{
74+
return true;
75+
}]
76+
>,
77+
InterfaceMethod<[{
78+
Handle the given inlined terminator by replacing it with a new operation
79+
as necessary. This overload is called when the inlined region has more
80+
than one block. The 'newDest' block represents the new final branching
81+
destination of blocks within this region, i.e. operations that release
82+
control to the parent operation will likely now branch to this block.
83+
Its block arguments correspond to any values that need to be replaced by
84+
terminators within the inlined region.
85+
}],
86+
"void", "handleTerminator",
87+
(ins "::mlir::Operation *":$op, "::mlir::Block *":$newDest),
88+
[{
89+
llvm_unreachable("must implement handleTerminator in the case of multiple "
90+
"inlined blocks");
91+
}]
92+
>,
93+
InterfaceMethod<[{
94+
Handle the given inlined terminator by replacing it with a new operation
95+
as necessary. This overload is called when the inlined region only
96+
contains one block. 'valuesToReplace' contains the previously returned
97+
values of the call site before inlining. These values must be replaced by
98+
this callback if they had any users (for example for traditional function
99+
calls, these are directly replaced with the operands of the `return`
100+
operation). The given 'op' will be removed by the caller, after this
101+
function has been called.
102+
}],
103+
"void", "handleTerminator",
104+
(ins "::mlir::Operation *":$op, "::mlir::ValueRange":$valuesToReplace),
105+
[{
106+
llvm_unreachable(
107+
"must implement handleTerminator in the case of one inlined block");
108+
}]
109+
>,
110+
InterfaceMethod<[{
111+
Attempt to materialize a conversion for a type mismatch between a call
112+
from this dialect, and a callable region. This method should generate an
113+
operation that takes 'input' as the only operand, and produces a single
114+
result of 'resultType'. If a conversion can not be generated, nullptr
115+
should be returned. For example, this hook may be invoked in the following
116+
scenarios:
117+
118+
```mlir
119+
func @foo(i32) -> i32 { ... }
120+
121+
// Mismatched input operand ... = foo.call @foo(%input : i16) -> i32
122+
123+
// Mismatched result type.
124+
... = foo.call @foo(%input : i32) -> i16
125+
```
126+
127+
NOTE: This hook may be invoked before the 'isLegal' checks above.
128+
}],
129+
"::mlir::Operation *", "materializeCallConversion",
130+
(ins "::mlir::OpBuilder &":$builder, "::mlir::Value":$input,
131+
"::mlir::Type":$resultType, "::mlir::Location":$conversionLoc),
132+
[{
133+
return nullptr;
134+
}]
135+
>,
136+
InterfaceMethod<[{
137+
Hook to transform the call arguments before using them to replace the
138+
callee arguments. Returns a value of the same type or the `argument`
139+
itself if nothing changed. The `argumentAttrs` dictionary is non-null even
140+
if no attribute is present. The hook is called after converting the
141+
callsite argument types using the materializeCallConversion callback, and
142+
right before inlining the callee region. Any operations created using the
143+
provided `builder` are inserted right before the inlined callee region. An
144+
example use case is the insertion of copies for by value arguments.
145+
}],
146+
"::mlir::Value", "handleArgument",
147+
(ins "::mlir::OpBuilder &":$builder, "::mlir::Operation *":$call,
148+
"::mlir::Operation *":$callable, "::mlir::Value":$argument,
149+
"::mlir::DictionaryAttr":$argumentAttrs),
150+
[{
151+
return argument;
152+
}]
153+
>,
154+
InterfaceMethod<[{
155+
Hook to transform the callee results before using them to replace the call
156+
results. Returns a value of the same type or the `result` itself if
157+
nothing changed. The `resultAttrs` dictionary is non-null even if no
158+
attribute is present. The hook is called right before handling
159+
terminators, and obtains the callee result before converting its type
160+
using the `materializeCallConversion` callback. Any operations created
161+
using the provided `builder` are inserted right after the inlined callee
162+
region. An example use case is the insertion of copies for by value
163+
results. NOTE: This hook is invoked after inlining the `callable` region.
164+
}],
165+
"::mlir::Value", "handleResult",
166+
(ins "::mlir::OpBuilder &":$builder, "::mlir::Operation *":$call,
167+
"::mlir::Operation *":$callable, "::mlir::Value":$result,
168+
"::mlir::DictionaryAttr":$resultAttrs),
169+
[{
170+
return result;
171+
}]
172+
>,
173+
InterfaceMethod<[{
174+
Process a set of blocks that have been inlined for a call. This callback
175+
is invoked before inlined terminator operations have been processed.
176+
}],
177+
"void", "processInlinedCallBlocks",
178+
(ins "::mlir::Operation *":$call,
179+
"::mlir::iterator_range<::mlir::Region::iterator>":$inlinedBlocks),
180+
[{}]
181+
>,
182+
InterfaceMethod<[{
183+
Returns true if the inliner can assume a fast path of not creating a new
184+
block, if there is only one block.
185+
}],
186+
"bool", "allowSingleBlockOptimization",
187+
(ins "::mlir::iterator_range<::mlir::Region::iterator>":$inlinedBlocks),
188+
[{
189+
return true;
190+
}]
191+
>
192+
];
193+
}
194+
195+
196+
#endif

0 commit comments

Comments
 (0)