Skip to content

Commit e2c6861

Browse files
committed
[MLIR][DLTI] Introduce DLTIQueryInterface and impl for DLTI attrs
This new interface is supposed to capture the core functionality of DLTI: querying for values at keys. As such this new interface unifies the ability to query DLTI attributes in a single method: query(). All existing DLTI interfaces exposing their own query methods now 1) now extend this new interface and 2) provide a default implementation for `query()`. As DLTIQueryInterface::query() returns an attribute, it naturally enables recursive queries on nested DLTI attrs. A utility function, `dlti::query()`, implements the logic for nested lookups. A new `#dlti.map` attribute is introduced to capture the most generic form of a finite DLTI-mapping. One of the benefits is that it allows for more easily encoding hierachical information that is suitably queryable, i.e. by means of nested attributes. In line with the above, `transform.dlti.query` is modified so as to take an arbitrary number of keys and to perform a nested lookup using the above utility function.
1 parent 7452014 commit e2c6861

File tree

12 files changed

+454
-169
lines changed

12 files changed

+454
-169
lines changed

mlir/include/mlir/Dialect/DLTI/DLTI.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,10 @@ class DataLayoutEntryAttrStorage;
2424
} // namespace mlir
2525
namespace mlir {
2626
namespace dlti {
27-
/// Find the first DataLayoutSpec associated to `op`, via either the
28-
/// DataLayoutOpInterface, a method on ModuleOp, or an attribute implementing
29-
/// the interface, on `op` and else on `op`'s ancestors in turn.
30-
DataLayoutSpecInterface getDataLayoutSpec(Operation *op);
31-
32-
/// Find the first TargetSystemSpec associated to `op`, via either the
33-
/// DataLayoutOpInterface, a method on ModuleOp, or an attribute implementing
34-
/// the interface, on `op` and else on `op`'s ancestors in turn.
35-
TargetSystemSpecInterface getTargetSystemSpec(Operation *op);
27+
/// Perform a DLTI-query at `op`, recursively querying each key of `keys` on
28+
/// query interface-implementing attrs, starting from attr obtained from `op`.
29+
FailureOr<Attribute> query(Operation *op, ArrayRef<StringAttr> keys,
30+
bool emitError = false);
3631
} // namespace dlti
3732
} // namespace mlir
3833

mlir/include/mlir/Dialect/DLTI/DLTIAttrs.td

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define MLIR_DIALECT_DLTI_DLTIATTRS_TD
1111

1212
include "mlir/Dialect/DLTI/DLTI.td"
13+
include "mlir/Interfaces/DataLayoutInterfaces.td"
1314
include "mlir/IR/AttrTypeBase.td"
1415

1516
class DLTIAttr<string name, list<Trait> traits = [],
@@ -20,13 +21,8 @@ class DLTIAttr<string name, list<Trait> traits = [],
2021
// DataLayoutEntryAttr
2122
//===----------------------------------------------------------------------===//
2223

23-
def DataLayoutEntryTrait
24-
: NativeAttrTrait<"DataLayoutEntryInterface::Trait"> {
25-
let cppNamespace = "::mlir";
26-
}
27-
2824
def DLTI_DataLayoutEntryAttr :
29-
DLTIAttr<"DataLayoutEntry", [DataLayoutEntryTrait]> {
25+
DLTIAttr<"DataLayoutEntry", [DataLayoutEntryInterface]> {
3026
let summary = "An attribute to represent an entry of a data layout specification.";
3127
let description = [{
3228
A data layout entry attribute is a key-value pair where the key is a type or
@@ -53,13 +49,9 @@ def DLTI_DataLayoutEntryAttr :
5349
//===----------------------------------------------------------------------===//
5450
// DataLayoutSpecAttr
5551
//===----------------------------------------------------------------------===//
56-
def DataLayoutSpecTrait
57-
: NativeAttrTrait<"DataLayoutSpecInterface::Trait"> {
58-
let cppNamespace = "::mlir";
59-
}
6052

6153
def DLTI_DataLayoutSpecAttr :
62-
DLTIAttr<"DataLayoutSpec", [DataLayoutSpecTrait]> {
54+
DLTIAttr<"DataLayoutSpec", [DataLayoutSpecInterface]> {
6355
let summary = "An attribute to represent a data layout specification.";
6456
let description = [{
6557
A data layout specification is a list of entries that specify (partial) data
@@ -78,7 +70,7 @@ def DLTI_DataLayoutSpecAttr :
7870
/// same key as the newer entries if the entries are compatible. Returns null
7971
/// if the specifications are not compatible.
8072
DataLayoutSpecAttr combineWith(ArrayRef<DataLayoutSpecInterface> specs) const;
81-
73+
8274
/// Returns the endiannes identifier.
8375
StringAttr getEndiannessIdentifier(MLIRContext *context) const;
8476

@@ -93,20 +85,78 @@ def DLTI_DataLayoutSpecAttr :
9385

9486
/// Returns the stack alignment identifier.
9587
StringAttr getStackAlignmentIdentifier(MLIRContext *context) const;
88+
89+
/// Returns the attribute associated with the key.
90+
FailureOr<Attribute> query(DataLayoutEntryKey key) {
91+
return llvm::cast<mlir::DataLayoutSpecInterface>(*this).queryHelper(key);
92+
}
93+
}];
94+
}
95+
96+
def DLTI_MapAttr : DLTIAttr<"Map", [DLTIQueryInterface]> {
97+
let summary = "A mapping of DLTI-information by way of key-value pairs";
98+
let description = [{
99+
A Data Layout and Target Information map is a list of entries effectively
100+
encoding a dictionary, mapping DLTI-related keys to DLTI-related values.
101+
102+
This attribute's main purpose is to facilate querying IR for arbitrary
103+
key-value associations that encode DLTI. Facility functions exist to perform
104+
recursive lookups on nested DLTI-map/query interface-implementing
105+
attributes.
106+
107+
Consider the following flat encoding of a single-key dictionary
108+
```
109+
#dlti.map<#dlti.dl_entry<"CPU::cache::L1::size_in_bytes", 65536 : i32>>
110+
```
111+
versus nested maps, which make it possible to obtain sub-dictionaries of
112+
related information (with the following example making use of other
113+
attributes that also implement the `DLTIQueryInterface`):
114+
```
115+
#dlti.target_system_spec<"CPU":
116+
#dlti.target_device_spec<#dlti.dl_entry<"cache",
117+
#dlti.map<#dlti.dl_entry<"L1",
118+
#dlti.map<#dlti.dl_entry<"size_in_bytes", 65536 : i32>>>,
119+
#dlti.dl_entry<"L1d",
120+
#dlti.map<#dlti.dl_entry<"size_in_bytes", 32768 : i32>>> >>>>
121+
```
122+
123+
With the flat encoding, the implied structure of the key is ignored, that is
124+
the only successful query (as expressed in the Transform Dialect) is:
125+
`transform.dlti.query ["CPU::cache::L1::size_in_bytes"] at %op`,
126+
where `%op` is a handle to an operation which associates the flat-encoding
127+
`#dlti.map` attribute.
128+
129+
For querying nested dictionaries, the relevant keys need to be separately
130+
provided. That is, if `%op` is an handle to an op which has the nesting
131+
`#dlti.target_system_spec`-attribute from above attached, then
132+
`transform.dlti.query ["CPU","cache","L1","size_in_bytes"] at %op` gives
133+
back the first leaf value contained. To access the other leaf, we need to do
134+
`transform.dlti.query ["CPU","cache","L1d","size_in_bytes"] at %op`.
135+
```
136+
}];
137+
let parameters = (ins
138+
ArrayRefParameter<"DataLayoutEntryInterface", "">:$entries
139+
);
140+
let mnemonic = "map";
141+
let genVerifyDecl = 1;
142+
let assemblyFormat = "`<` $entries `>`";
143+
let extraClassDeclaration = [{
144+
/// Returns the attribute associated with the key.
145+
FailureOr<Attribute> query(DataLayoutEntryKey key) {
146+
for (DataLayoutEntryInterface entry : getEntries())
147+
if (entry.getKey() == key)
148+
return entry.getValue();
149+
return ::mlir::failure();
150+
}
96151
}];
97152
}
98153

99154
//===----------------------------------------------------------------------===//
100155
// TargetSystemSpecAttr
101156
//===----------------------------------------------------------------------===//
102157

103-
def TargetSystemSpecTrait
104-
: NativeAttrTrait<"TargetSystemSpecInterface::Trait"> {
105-
let cppNamespace = "::mlir";
106-
}
107-
108158
def DLTI_TargetSystemSpecAttr :
109-
DLTIAttr<"TargetSystemSpec", [TargetSystemSpecTrait]> {
159+
DLTIAttr<"TargetSystemSpec", [TargetSystemSpecInterface]> {
110160
let summary = "An attribute to represent target system specification.";
111161
let description = [{
112162
A system specification describes the overall system containing
@@ -136,6 +186,11 @@ def DLTI_TargetSystemSpecAttr :
136186
std::optional<TargetDeviceSpecInterface>
137187
getDeviceSpecForDeviceID(
138188
TargetSystemSpecInterface::DeviceID deviceID);
189+
190+
/// Returns the attribute associated with the key.
191+
FailureOr<Attribute> query(DataLayoutEntryKey key) const {
192+
return llvm::cast<mlir::TargetSystemSpecInterface>(*this).queryHelper(key);
193+
}
139194
}];
140195
let extraClassDefinition = [{
141196
std::optional<TargetDeviceSpecInterface>
@@ -154,13 +209,8 @@ def DLTI_TargetSystemSpecAttr :
154209
// TargetDeviceSpecAttr
155210
//===----------------------------------------------------------------------===//
156211

157-
def TargetDeviceSpecTrait
158-
: NativeAttrTrait<"TargetDeviceSpecInterface::Trait"> {
159-
let cppNamespace = "::mlir";
160-
}
161-
162212
def DLTI_TargetDeviceSpecAttr :
163-
DLTIAttr<"TargetDeviceSpec", [TargetDeviceSpecTrait]> {
213+
DLTIAttr<"TargetDeviceSpec", [TargetDeviceSpecInterface]> {
164214
let summary = "An attribute to represent target device specification.";
165215
let description = [{
166216
Each device specification describes a single device and its
@@ -179,6 +229,13 @@ def DLTI_TargetDeviceSpecAttr :
179229
let mnemonic = "target_device_spec";
180230
let genVerifyDecl = 1;
181231
let assemblyFormat = "`<` $entries `>`";
232+
233+
let extraClassDeclaration = [{
234+
/// Returns the attribute associated with the key.
235+
FailureOr<Attribute> query(DataLayoutEntryKey key) const {
236+
return llvm::cast<mlir::TargetDeviceSpecInterface>(*this).queryHelper(key);
237+
}
238+
}];
182239
}
183240

184241
#endif // MLIR_DIALECT_DLTI_DLTIATTRS_TD

mlir/include/mlir/Dialect/DLTI/DLTIBase.td

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@ def DLTI_Dialect : Dialect {
2323
}];
2424

2525
let extraClassDeclaration = [{
26-
// Top level attribute name.
26+
// Top-level attribute name for arbitrary description.
27+
constexpr const static ::llvm::StringLiteral
28+
kMapAttrName = "dlti.map";
29+
30+
// Top-level attribute name for data layout description.
2731
constexpr const static ::llvm::StringLiteral
2832
kDataLayoutAttrName = "dlti.dl_spec";
2933

30-
// Top level attribute name for target system description
34+
// Top-level attribute name for target system description.
3135
constexpr const static ::llvm::StringLiteral
3236
kTargetSystemDescAttrName = "dlti.target_system_spec";
3337

38+
// Top-level attribute name for target device description.
3439
constexpr const static ::llvm::StringLiteral
3540
kTargetDeviceDescAttrName = "dlti.target_device_spec";
3641

mlir/include/mlir/Dialect/DLTI/TransformOps/DLTITransformOps.td

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,40 @@ def QueryOp : Op<Transform_Dialect, "dlti.query", [
2222
let summary = "Return attribute (as param) associated to key via DTLI";
2323
let description = [{
2424
This op queries data layout and target information associated to payload
25-
IR by way of the DLTI dialect. A lookup is performed for the given `key`
26-
at the `target` op, with the DLTI dialect determining which interfaces and
27-
attributes are consulted - first checking `target` and then its ancestors.
25+
IR by way of the DLTI dialect.
2826

29-
When only `key` is provided, the lookup occurs with respect to the data
30-
layout specification of DLTI. When `device` is provided, the lookup occurs
31-
with respect to DLTI's target device specifications associated to a DLTI
32-
system device specification.
27+
A lookup is performed for the given `keys` at `target` op - or its closest
28+
interface-implementing ancestor - by way of the `DLTIQueryInterface`, which
29+
returns an attribute for a key. If more than one key is provided, the lookup
30+
continues recursively, now on the returned attributes, with the condition
31+
that these implement the above interface. For example if the payload IR is
32+
33+
```
34+
module attributes {#dlti.map = #dlti.map<#dlti.dl_entry<"A",
35+
#dlti.map<#dlti.dl_entry<"B", 42: int>>>} {
36+
func.func private @f()
37+
}
38+
```
39+
and we have that `%func` is a Tranform handle to op `@f`, then
40+
`transform.dlti.query ["A", "B"] at %func` returns 42 as a param and
41+
`transform.dlti.query ["A"] at %func` returns the `#dlti.map` attribute
42+
containing just the key "B" and its value. Using `["B"]` or `["A","C"]` as
43+
`keys` will yield an error.
3344

3445
#### Return modes
3546

36-
When succesful, the result, `associated_attr`, associates one attribute as a
37-
param for each op in `target`'s payload.
47+
When successful, the result, `associated_attr`, associates one attribute as
48+
a param for each op in `target`'s payload.
3849

39-
If the lookup fails - as DLTI specifications or entries with the right
40-
names are missing (i.e. the values of `device` and `key`) - a definite
41-
failure is returned.
50+
If the lookup fails - as no DLTI attributes/interfaces are found or entries
51+
with the right names are missing - a silenceable failure is returned.
4252
}];
4353

4454
let arguments = (ins TransformHandleTypeInterface:$target,
45-
OptionalAttr<StrAttr>:$device,
46-
StrAttr:$key);
55+
StrArrayAttr:$keys);
4756
let results = (outs TransformParamTypeInterface:$associated_attr);
4857
let assemblyFormat =
49-
"(`:``:` $device^ `:``:`)? $key `at` $target attr-dict `:`"
50-
"functional-type(operands, results)";
58+
"$keys `at` $target attr-dict `:` functional-type(operands, results)";
5159

5260
let extraClassDeclaration = [{
5361
::mlir::DiagnosedSilenceableFailure applyToOne(

mlir/include/mlir/Interfaces/DataLayoutInterfaces.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
namespace mlir {
2424
class DataLayout;
2525
class DataLayoutEntryInterface;
26+
class DLTIQueryInterface;
2627
class TargetDeviceSpecInterface;
2728
class TargetSystemSpecInterface;
2829
using DataLayoutEntryKey = llvm::PointerUnion<Type, StringAttr>;

mlir/include/mlir/Interfaces/DataLayoutInterfaces.td

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,29 @@ include "mlir/IR/OpBase.td"
2020
// Attribute interfaces
2121
//===----------------------------------------------------------------------===//
2222

23+
def DLTIQueryInterface : AttrInterface<"DLTIQueryInterface"> {
24+
let cppNamespace = "::mlir";
25+
26+
let description = [{
27+
Attribute interface exposing querying-mechanism for key-value associations.
28+
29+
The central feature of DLTI attributes is to allow looking up values at
30+
keys. This interface represent the core functionality to do so - as such
31+
most DLTI attributes should be implementing this interface.
32+
33+
Note that as the `query` method returns an attribute, this attribute can
34+
be recursively queried when it also implements this interface.
35+
}];
36+
let methods = [
37+
InterfaceMethod<
38+
/*description=*/"Returns the attribute associated with the key.",
39+
/*retTy=*/"::mlir::FailureOr<::mlir::Attribute>",
40+
/*methodName=*/"query",
41+
/*args=*/(ins "::mlir::DataLayoutEntryKey":$key)
42+
>
43+
];
44+
}
45+
2346
def DataLayoutEntryInterface : AttrInterface<"DataLayoutEntryInterface"> {
2447
let cppNamespace = "::mlir";
2548

@@ -68,7 +91,7 @@ def DataLayoutEntryInterface : AttrInterface<"DataLayoutEntryInterface"> {
6891
}];
6992
}
7093

71-
def DataLayoutSpecInterface : AttrInterface<"DataLayoutSpecInterface"> {
94+
def DataLayoutSpecInterface : AttrInterface<"DataLayoutSpecInterface", [DLTIQueryInterface]> {
7295
let cppNamespace = "::mlir";
7396

7497
let description = [{
@@ -173,7 +196,7 @@ def DataLayoutSpecInterface : AttrInterface<"DataLayoutSpecInterface"> {
173196
/*defaultImplementation=*/[{
174197
return ::mlir::detail::verifyDataLayoutSpec($_attr, loc);
175198
}]
176-
>,
199+
>
177200
];
178201

179202
let extraClassDeclaration = [{
@@ -184,6 +207,15 @@ def DataLayoutSpecInterface : AttrInterface<"DataLayoutSpecInterface"> {
184207
return getSpecForType(TypeID::get<Ty>());
185208
}
186209

210+
/// Helper for default implementation of `DLTIQueryInterface`'s `query`.
211+
inline ::mlir::FailureOr<::mlir::Attribute>
212+
queryHelper(::mlir::DataLayoutEntryKey key) const {
213+
for (DataLayoutEntryInterface entry : getEntries())
214+
if (entry.getKey() == key)
215+
return entry.getValue();
216+
return ::mlir::failure();
217+
}
218+
187219
/// Populates the given maps with lists of entries grouped by the type or
188220
/// identifier they are associated with. Users are not expected to call this
189221
/// method directly.
@@ -194,7 +226,7 @@ def DataLayoutSpecInterface : AttrInterface<"DataLayoutSpecInterface"> {
194226
}];
195227
}
196228

197-
def TargetDeviceSpecInterface : AttrInterface<"TargetDeviceSpecInterface"> {
229+
def TargetDeviceSpecInterface : AttrInterface<"TargetDeviceSpecInterface", [DLTIQueryInterface]> {
198230
let cppNamespace = "::mlir";
199231

200232
let description = [{
@@ -239,9 +271,20 @@ def TargetDeviceSpecInterface : AttrInterface<"TargetDeviceSpecInterface"> {
239271
/*defaultImplementation=*/[{ return ::mlir::success(); }]
240272
>
241273
];
274+
275+
let extraClassDeclaration = [{
276+
/// Helper for default implementation of `DLTIQueryInterface`'s `query`.
277+
::mlir::FailureOr<::mlir::Attribute>
278+
queryHelper(::mlir::DataLayoutEntryKey key) const {
279+
if (auto strKey = llvm::dyn_cast<StringAttr>(key))
280+
if (DataLayoutEntryInterface spec = getSpecForIdentifier(strKey))
281+
return spec.getValue();
282+
return ::mlir::failure();
283+
}
284+
}];
242285
}
243286

244-
def TargetSystemSpecInterface : AttrInterface<"TargetSystemSpecInterface"> {
287+
def TargetSystemSpecInterface : AttrInterface<"TargetSystemSpecInterface", [DLTIQueryInterface]> {
245288
let cppNamespace = "::mlir";
246289

247290
let description = [{
@@ -287,6 +330,15 @@ def TargetSystemSpecInterface : AttrInterface<"TargetSystemSpecInterface"> {
287330

288331
let extraClassDeclaration = [{
289332
using DeviceID = StringAttr;
333+
334+
/// Helper for default implementation of `DLTIQueryInterface`'s `query`.
335+
::mlir::FailureOr<::mlir::Attribute>
336+
queryHelper(::mlir::DataLayoutEntryKey key) const {
337+
if (auto strKey = llvm::dyn_cast<::mlir::StringAttr>(key))
338+
if (auto deviceSpec = getDeviceSpecForDeviceID(strKey))
339+
return *deviceSpec;
340+
return ::mlir::failure();
341+
}
290342
}];
291343
}
292344

0 commit comments

Comments
 (0)