MLIR  20.0.0git
DLTI.cpp
Go to the documentation of this file.
1 //===- DLTI.cpp - Data Layout And Target Info MLIR Dialect Implementation -===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "mlir/IR/Builders.h"
11 #include "mlir/IR/BuiltinDialect.h"
12 #include "mlir/IR/BuiltinOps.h"
13 #include "mlir/IR/BuiltinTypes.h"
14 #include "mlir/IR/Dialect.h"
16 #include "llvm/ADT/TypeSwitch.h"
17 
18 #include "llvm/ADT/TypeSwitch.h"
19 #include "llvm/Support/Debug.h"
20 #include "llvm/Support/MathExtras.h"
21 
22 using namespace mlir;
23 
24 #include "mlir/Dialect/DLTI/DLTIDialect.cpp.inc"
25 
26 #define GET_ATTRDEF_CLASSES
27 #include "mlir/Dialect/DLTI/DLTIAttrs.cpp.inc"
28 
29 #define DEBUG_TYPE "dlti"
30 
31 //===----------------------------------------------------------------------===//
32 // DataLayoutEntryAttr
33 //===----------------------------------------------------------------------===//
34 namespace mlir {
35 namespace detail {
37 public:
38  using KeyTy = std::pair<DataLayoutEntryKey, Attribute>;
39 
42 
44  construct(AttributeStorageAllocator &allocator, const KeyTy &key) {
45  return new (allocator.allocate<DataLayoutEntryAttrStorage>())
46  DataLayoutEntryAttrStorage(key.first, key.second);
47  }
48 
49  bool operator==(const KeyTy &other) const {
50  return other.first == entryKey && other.second == value;
51  }
52 
55 };
56 } // namespace detail
57 } // namespace mlir
58 
59 DataLayoutEntryAttr DataLayoutEntryAttr::get(StringAttr key, Attribute value) {
60  return Base::get(key.getContext(), key, value);
61 }
62 
63 DataLayoutEntryAttr DataLayoutEntryAttr::get(Type key, Attribute value) {
64  return Base::get(key.getContext(), key, value);
65 }
66 
67 DataLayoutEntryKey DataLayoutEntryAttr::getKey() const {
68  return getImpl()->entryKey;
69 }
70 
71 Attribute DataLayoutEntryAttr::getValue() const { return getImpl()->value; }
72 
73 /// Parses an attribute with syntax:
74 /// attr ::= `#target.` `dl_entry` `<` (type | quoted-string) `,` attr `>`
76  if (failed(parser.parseLess()))
77  return {};
78 
79  Type type = nullptr;
80  std::string identifier;
81  SMLoc idLoc = parser.getCurrentLocation();
82  OptionalParseResult parsedType = parser.parseOptionalType(type);
83  if (parsedType.has_value() && failed(parsedType.value()))
84  return {};
85  if (!parsedType.has_value()) {
86  OptionalParseResult parsedString = parser.parseOptionalString(&identifier);
87  if (!parsedString.has_value() || failed(parsedString.value())) {
88  parser.emitError(idLoc) << "expected a type or a quoted string";
89  return {};
90  }
91  }
92 
93  Attribute value;
94  if (failed(parser.parseComma()) || failed(parser.parseAttribute(value)) ||
95  failed(parser.parseGreater()))
96  return {};
97 
98  return type ? get(type, value)
99  : get(parser.getBuilder().getStringAttr(identifier), value);
100 }
101 
102 void DataLayoutEntryAttr::print(AsmPrinter &os) const {
103  os << "<";
104  if (auto type = llvm::dyn_cast_if_present<Type>(getKey()))
105  os << type;
106  else
107  os << "\"" << getKey().get<StringAttr>().strref() << "\"";
108  os << ", " << getValue() << ">";
109 }
110 
111 //===----------------------------------------------------------------------===//
112 // DataLayoutSpecAttr
113 //===----------------------------------------------------------------------===//
114 
115 LogicalResult
118  DenseSet<Type> types;
120  for (DataLayoutEntryInterface entry : entries) {
121  if (auto type = llvm::dyn_cast_if_present<Type>(entry.getKey())) {
122  if (!types.insert(type).second)
123  return emitError() << "repeated layout entry key: " << type;
124  } else {
125  auto id = entry.getKey().get<StringAttr>();
126  if (!ids.insert(id).second)
127  return emitError() << "repeated layout entry key: " << id.getValue();
128  }
129  }
130  return success();
131 }
132 
133 /// Given a list of old and a list of new entries, overwrites old entries with
134 /// new ones if they have matching keys, appends new entries to the old entry
135 /// list otherwise.
136 static void
139  unsigned oldEntriesSize = oldEntries.size();
140  for (DataLayoutEntryInterface entry : newEntries) {
141  // We expect a small (dozens) number of entries, so it is practically
142  // cheaper to iterate over the list linearly rather than to create an
143  // auxiliary hashmap to avoid duplication. Also note that we never need to
144  // check for duplicate keys the values that were added from `newEntries`.
145  bool replaced = false;
146  for (unsigned i = 0; i < oldEntriesSize; ++i) {
147  if (oldEntries[i].getKey() == entry.getKey()) {
148  oldEntries[i] = entry;
149  replaced = true;
150  break;
151  }
152  }
153  if (!replaced)
154  oldEntries.push_back(entry);
155  }
156 }
157 
158 /// Combines a data layout spec into the given lists of entries organized by
159 /// type class and identifier, overwriting them if necessary. Fails to combine
160 /// if the two entries with identical keys are not compatible.
161 static LogicalResult
162 combineOneSpec(DataLayoutSpecInterface spec,
165  // A missing spec should be fine.
166  if (!spec)
167  return success();
168 
169  DenseMap<TypeID, DataLayoutEntryList> newEntriesForType;
171  spec.bucketEntriesByType(newEntriesForType, newEntriesForID);
172 
173  // Try overwriting the old entries with the new ones.
174  for (auto &kvp : newEntriesForType) {
175  if (!entriesForType.count(kvp.first)) {
176  entriesForType[kvp.first] = std::move(kvp.second);
177  continue;
178  }
179 
180  Type typeSample = kvp.second.front().getKey().get<Type>();
181  assert(&typeSample.getDialect() !=
182  typeSample.getContext()->getLoadedDialect<BuiltinDialect>() &&
183  "unexpected data layout entry for built-in type");
184 
185  auto interface = llvm::cast<DataLayoutTypeInterface>(typeSample);
186  if (!interface.areCompatible(entriesForType.lookup(kvp.first), kvp.second))
187  return failure();
188 
189  overwriteDuplicateEntries(entriesForType[kvp.first], kvp.second);
190  }
191 
192  for (const auto &kvp : newEntriesForID) {
193  StringAttr id = kvp.second.getKey().get<StringAttr>();
194  Dialect *dialect = id.getReferencedDialect();
195  if (!entriesForID.count(id)) {
196  entriesForID[id] = kvp.second;
197  continue;
198  }
199 
200  // Attempt to combine the enties using the dialect interface. If the
201  // dialect is not loaded for some reason, use the default combinator
202  // that conservatively accepts identical entries only.
203  entriesForID[id] =
204  dialect ? cast<DataLayoutDialectInterface>(dialect)->combine(
205  entriesForID[id], kvp.second)
207  kvp.second);
208  if (!entriesForID[id])
209  return failure();
210  }
211 
212  return success();
213 }
214 
215 DataLayoutSpecAttr
216 DataLayoutSpecAttr::combineWith(ArrayRef<DataLayoutSpecInterface> specs) const {
217  // Only combine with attributes of the same kind.
218  // TODO: reconsider this when the need arises.
219  if (llvm::any_of(specs, [](DataLayoutSpecInterface spec) {
220  return !llvm::isa<DataLayoutSpecAttr>(spec);
221  }))
222  return {};
223 
224  // Combine all specs in order, with `this` being the last one.
227  for (DataLayoutSpecInterface spec : specs)
228  if (failed(combineOneSpec(spec, entriesForType, entriesForID)))
229  return nullptr;
230  if (failed(combineOneSpec(*this, entriesForType, entriesForID)))
231  return nullptr;
232 
233  // Rebuild the linear list of entries.
235  llvm::append_range(entries, llvm::make_second_range(entriesForID));
236  for (const auto &kvp : entriesForType)
237  llvm::append_range(entries, kvp.getSecond());
238 
239  return DataLayoutSpecAttr::get(getContext(), entries);
240 }
241 
242 StringAttr
243 DataLayoutSpecAttr::getEndiannessIdentifier(MLIRContext *context) const {
244  return Builder(context).getStringAttr(DLTIDialect::kDataLayoutEndiannessKey);
245 }
246 
247 StringAttr
248 DataLayoutSpecAttr::getAllocaMemorySpaceIdentifier(MLIRContext *context) const {
249  return Builder(context).getStringAttr(
250  DLTIDialect::kDataLayoutAllocaMemorySpaceKey);
251 }
252 
253 StringAttr DataLayoutSpecAttr::getProgramMemorySpaceIdentifier(
254  MLIRContext *context) const {
255  return Builder(context).getStringAttr(
256  DLTIDialect::kDataLayoutProgramMemorySpaceKey);
257 }
258 
259 StringAttr
260 DataLayoutSpecAttr::getGlobalMemorySpaceIdentifier(MLIRContext *context) const {
261  return Builder(context).getStringAttr(
262  DLTIDialect::kDataLayoutGlobalMemorySpaceKey);
263 }
264 
265 StringAttr
266 DataLayoutSpecAttr::getStackAlignmentIdentifier(MLIRContext *context) const {
267  return Builder(context).getStringAttr(
268  DLTIDialect::kDataLayoutStackAlignmentKey);
269 }
270 
271 /// Parses an attribute with syntax
272 /// attr ::= `#target.` `dl_spec` `<` attr-list? `>`
273 /// attr-list ::= attr
274 /// | attr `,` attr-list
276  if (failed(parser.parseLess()))
277  return {};
278 
279  // Empty spec.
280  if (succeeded(parser.parseOptionalGreater()))
281  return get(parser.getContext(), {});
282 
284  if (parser.parseCommaSeparatedList(
285  [&]() { return parser.parseAttribute(entries.emplace_back()); }) ||
286  parser.parseGreater())
287  return {};
288 
289  return getChecked([&] { return parser.emitError(parser.getNameLoc()); },
290  parser.getContext(), entries);
291 }
292 
293 void DataLayoutSpecAttr::print(AsmPrinter &os) const {
294  os << "<";
295  llvm::interleaveComma(getEntries(), os);
296  os << ">";
297 }
298 
299 //===----------------------------------------------------------------------===//
300 // TargetDeviceSpecAttr
301 //===----------------------------------------------------------------------===//
302 
303 namespace mlir {
304 /// A FieldParser for key-value pairs of DeviceID-target device spec pairs that
305 /// make up a target system spec.
306 template <>
308  static FailureOr<DeviceIDTargetDeviceSpecPair> parse(AsmParser &parser) {
309  std::string deviceID;
310 
311  if (failed(parser.parseString(&deviceID))) {
312  parser.emitError(parser.getCurrentLocation())
313  << "DeviceID is missing, or is not of string type";
314  return failure();
315  }
316 
317  if (failed(parser.parseColon())) {
318  parser.emitError(parser.getCurrentLocation()) << "Missing colon";
319  return failure();
320  }
321 
322  auto target_device_spec =
324  if (failed(target_device_spec)) {
325  parser.emitError(parser.getCurrentLocation())
326  << "Error in parsing target device spec";
327  return failure();
328  }
329 
330  return std::make_pair(parser.getBuilder().getStringAttr(deviceID),
331  *target_device_spec);
332  }
333 };
334 
337  return printer << param.first << " : " << param.second;
338 }
339 
340 } // namespace mlir
341 
342 LogicalResult
345  // Entries in a target device spec can only have StringAttr as key. It does
346  // not support type as a key. Hence not reusing
347  // DataLayoutEntryInterface::verify.
349  for (DataLayoutEntryInterface entry : entries) {
350  if (auto type = llvm::dyn_cast_if_present<Type>(entry.getKey())) {
351  return emitError()
352  << "dlti.target_device_spec does not allow type as a key: "
353  << type;
354  } else {
355  // Check that keys in a target device spec are unique.
356  auto id = entry.getKey().get<StringAttr>();
357  if (!ids.insert(id).second)
358  return emitError() << "repeated layout entry key: " << id.getValue();
359  }
360  }
361 
362  return success();
363 }
364 
365 //===----------------------------------------------------------------------===//
366 // TargetSystemSpecAttr
367 //===----------------------------------------------------------------------===//
368 
369 LogicalResult
373 
374  for (const auto &entry : entries) {
375  TargetDeviceSpecInterface target_device_spec = entry.second;
376 
377  // First verify that a target device spec is valid.
379  target_device_spec.getEntries())))
380  return failure();
381 
382  // Check that device IDs are unique across all entries.
383  TargetSystemSpecInterface::DeviceID device_id = entry.first;
384  if (!device_ids.insert(device_id).second) {
385  return emitError() << "repeated Device ID in dlti.target_system_spec: "
386  << device_id;
387  }
388  }
389  return success();
390 }
391 
392 //===----------------------------------------------------------------------===//
393 // DLTIDialect
394 //===----------------------------------------------------------------------===//
395 
396 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutAttrName;
397 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessKey;
398 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessBig;
399 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessLittle;
400 
401 namespace {
402 class TargetDataLayoutInterface : public DataLayoutDialectInterface {
403 public:
405 
406  LogicalResult verifyEntry(DataLayoutEntryInterface entry,
407  Location loc) const final {
408  StringRef entryName = entry.getKey().get<StringAttr>().strref();
409  if (entryName == DLTIDialect::kDataLayoutEndiannessKey) {
410  auto value = llvm::dyn_cast<StringAttr>(entry.getValue());
411  if (value &&
412  (value.getValue() == DLTIDialect::kDataLayoutEndiannessBig ||
413  value.getValue() == DLTIDialect::kDataLayoutEndiannessLittle))
414  return success();
415  return emitError(loc) << "'" << entryName
416  << "' data layout entry is expected to be either '"
417  << DLTIDialect::kDataLayoutEndiannessBig << "' or '"
418  << DLTIDialect::kDataLayoutEndiannessLittle << "'";
419  }
420  if (entryName == DLTIDialect::kDataLayoutAllocaMemorySpaceKey ||
421  entryName == DLTIDialect::kDataLayoutProgramMemorySpaceKey ||
422  entryName == DLTIDialect::kDataLayoutGlobalMemorySpaceKey ||
423  entryName == DLTIDialect::kDataLayoutStackAlignmentKey)
424  return success();
425  return emitError(loc) << "unknown data layout entry name: " << entryName;
426  }
427 };
428 } // namespace
429 
430 void DLTIDialect::initialize() {
431  addAttributes<
432 #define GET_ATTRDEF_LIST
433 #include "mlir/Dialect/DLTI/DLTIAttrs.cpp.inc"
434  >();
435  addInterfaces<TargetDataLayoutInterface>();
436 }
437 
438 LogicalResult DLTIDialect::verifyOperationAttribute(Operation *op,
439  NamedAttribute attr) {
440  if (attr.getName() == DLTIDialect::kDataLayoutAttrName) {
441  if (!llvm::isa<DataLayoutSpecAttr>(attr.getValue())) {
442  return op->emitError() << "'" << DLTIDialect::kDataLayoutAttrName
443  << "' is expected to be a #dlti.dl_spec attribute";
444  }
445  if (isa<ModuleOp>(op))
446  return detail::verifyDataLayoutOp(op);
447  return success();
448  } else if (attr.getName() == DLTIDialect::kTargetSystemDescAttrName) {
449  if (!llvm::isa<TargetSystemSpecAttr>(attr.getValue())) {
450  return op->emitError()
451  << "'" << DLTIDialect::kTargetSystemDescAttrName
452  << "' is expected to be a #dlti.target_system_spec attribute";
453  }
454  return success();
455  }
456 
457  return op->emitError() << "attribute '" << attr.getName().getValue()
458  << "' not supported by dialect";
459 }
static LogicalResult combineOneSpec(DataLayoutSpecInterface spec, DenseMap< TypeID, DataLayoutEntryList > &entriesForType, DenseMap< StringAttr, DataLayoutEntryInterface > &entriesForID)
Combines a data layout spec into the given lists of entries organized by type class and identifier,...
Definition: DLTI.cpp:162
static void overwriteDuplicateEntries(SmallVectorImpl< DataLayoutEntryInterface > &oldEntries, ArrayRef< DataLayoutEntryInterface > newEntries)
Given a list of old and a list of new entries, overwrites old entries with new ones if they have matc...
Definition: DLTI.cpp:137
static MLIRContext * getContext(OpFoldResult val)
static void print(spirv::VerCapExtAttr triple, DialectAsmPrinter &printer)
This base class exposes generic asm parser hooks, usable across the various derived parsers.
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual OptionalParseResult parseOptionalType(Type &result)=0
Parse an optional type.
MLIRContext * getContext() const
Definition: AsmPrinter.cpp:73
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseLess()=0
Parse a '<' token.
ParseResult parseString(std::string *string)
Parse a quoted string token.
virtual ParseResult parseOptionalGreater()=0
Parse a '>' token if present.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseColon()=0
Parse a : token.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseOptionalString(std::string *string)=0
Parse a quoted string token if present.
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
This base class exposes generic asm printer hooks, usable across the various derived printers.
Base storage class appearing in an attribute.
Attributes are known-constant values of operations.
Definition: Attributes.h:25
This class is a general helper class for creating context-global objects like types,...
Definition: Builders.h:50
StringAttr getStringAttr(const Twine &bytes)
Definition: Builders.cpp:273
An interface to be implemented by dialects that can have identifiers in the data layout specification...
static DataLayoutEntryInterface defaultCombine(DataLayoutEntryInterface outer, DataLayoutEntryInterface inner)
Default implementation of entry combination that combines identical entries and returns null otherwis...
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition: Dialect.h:38
This class represents a diagnostic that is inflight and set to be reported.
Definition: Diagnostics.h:313
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:63
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
Dialect * getLoadedDialect(StringRef name)
Get a registered IR dialect with the given namespace.
NamedAttribute represents a combination of a name and an Attribute value.
Definition: Attributes.h:207
StringAttr getName() const
Return the name of the attribute.
Definition: Attributes.cpp:49
Attribute getValue() const
Return the value of the attribute.
Definition: Attributes.h:221
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:268
This class implements Optional functionality for ParseResult.
Definition: OpDefinition.h:39
ParseResult value() const
Access the internal ParseResult value.
Definition: OpDefinition.h:52
bool has_value() const
Returns true if we contain a valid ParseResult value.
Definition: OpDefinition.h:49
This is a utility allocator used to allocate memory for instances of derived types.
T * allocate()
Allocate an instance of the provided type.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
Dialect & getDialect() const
Get the dialect this type is registered to.
Definition: Types.h:123
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition: Types.cpp:35
DataLayoutEntryAttrStorage(DataLayoutEntryKey entryKey, Attribute value)
Definition: DLTI.cpp:40
bool operator==(const KeyTy &other) const
Definition: DLTI.cpp:49
std::pair< DataLayoutEntryKey, Attribute > KeyTy
Definition: DLTI.cpp:38
static DataLayoutEntryAttrStorage * construct(AttributeStorageAllocator &allocator, const KeyTy &key)
Definition: DLTI.cpp:44
LogicalResult verifyDataLayoutOp(Operation *op)
Verifies that the operation implementing the data layout interface, or a module operation,...
QueryRef parse(llvm::StringRef line, const QuerySession &qs)
Definition: Query.cpp:20
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
std::pair< StringAttr, TargetDeviceSpecInterface > DeviceIDTargetDeviceSpecPair
LogicalResult verify(Operation *op, bool verifyRecursively=true)
Perform (potentially expensive) checks of invariants, used to detect compiler bugs,...
Definition: Verifier.cpp:421
raw_ostream & operator<<(raw_ostream &os, const AliasResult &result)
Definition: AliasAnalysis.h:78
static FailureOr< DeviceIDTargetDeviceSpecPair > parse(AsmParser &parser)
Definition: DLTI.cpp:308
Provide a template class that can be specialized by users to dispatch to parsers.