MLIR  19.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/Dialect.h"
15 #include "llvm/ADT/TypeSwitch.h"
16 
17 using namespace mlir;
18 
19 #include "mlir/Dialect/DLTI/DLTIDialect.cpp.inc"
20 
21 //===----------------------------------------------------------------------===//
22 // DataLayoutEntryAttr
23 //===----------------------------------------------------------------------===//
24 //
25 constexpr const StringLiteral mlir::DataLayoutEntryAttr::kAttrKeyword;
26 
27 namespace mlir {
28 namespace impl {
30 public:
31  using KeyTy = std::pair<DataLayoutEntryKey, Attribute>;
32 
35 
37  const KeyTy &key) {
38  return new (allocator.allocate<DataLayoutEntryStorage>())
39  DataLayoutEntryStorage(key.first, key.second);
40  }
41 
42  bool operator==(const KeyTy &other) const {
43  return other.first == entryKey && other.second == value;
44  }
45 
48 };
49 } // namespace impl
50 } // namespace mlir
51 
53  return Base::get(key.getContext(), key, value);
54 }
55 
57  return Base::get(key.getContext(), key, value);
58 }
59 
61  return getImpl()->entryKey;
62 }
63 
64 Attribute DataLayoutEntryAttr::getValue() const { return getImpl()->value; }
65 
66 /// Parses an attribute with syntax:
67 /// attr ::= `#target.` `dl_entry` `<` (type | quoted-string) `,` attr `>`
69  if (failed(parser.parseLess()))
70  return {};
71 
72  Type type = nullptr;
73  std::string identifier;
74  SMLoc idLoc = parser.getCurrentLocation();
75  OptionalParseResult parsedType = parser.parseOptionalType(type);
76  if (parsedType.has_value() && failed(parsedType.value()))
77  return {};
78  if (!parsedType.has_value()) {
79  OptionalParseResult parsedString = parser.parseOptionalString(&identifier);
80  if (!parsedString.has_value() || failed(parsedString.value())) {
81  parser.emitError(idLoc) << "expected a type or a quoted string";
82  return {};
83  }
84  }
85 
86  Attribute value;
87  if (failed(parser.parseComma()) || failed(parser.parseAttribute(value)) ||
88  failed(parser.parseGreater()))
89  return {};
90 
91  return type ? get(type, value)
92  : get(parser.getBuilder().getStringAttr(identifier), value);
93 }
94 
97  if (auto type = llvm::dyn_cast_if_present<Type>(getKey()))
98  os << type;
99  else
100  os << "\"" << getKey().get<StringAttr>().strref() << "\"";
101  os << ", " << getValue() << ">";
102 }
103 
104 //===----------------------------------------------------------------------===//
105 // DataLayoutSpecAttr
106 //===----------------------------------------------------------------------===//
107 //
108 constexpr const StringLiteral mlir::DataLayoutSpecAttr::kAttrKeyword;
109 constexpr const StringLiteral
110  mlir::DLTIDialect::kDataLayoutAllocaMemorySpaceKey;
111 constexpr const StringLiteral
112  mlir::DLTIDialect::kDataLayoutProgramMemorySpaceKey;
113 constexpr const StringLiteral
114  mlir::DLTIDialect::kDataLayoutGlobalMemorySpaceKey;
115 
116 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutStackAlignmentKey;
117 
118 namespace mlir {
119 namespace impl {
121 public:
123 
125  : entries(entries) {}
126 
127  bool operator==(const KeyTy &key) const { return key == entries; }
128 
130  const KeyTy &key) {
131  return new (allocator.allocate<DataLayoutSpecStorage>())
132  DataLayoutSpecStorage(allocator.copyInto(key));
133  }
134 
136 };
137 } // namespace impl
138 } // namespace mlir
139 
143  return Base::get(ctx, entries);
144 }
145 
148  MLIRContext *context,
150  return Base::getChecked(emitError, context, entries);
151 }
152 
156  DenseSet<Type> types;
158  for (DataLayoutEntryInterface entry : entries) {
159  if (auto type = llvm::dyn_cast_if_present<Type>(entry.getKey())) {
160  if (!types.insert(type).second)
161  return emitError() << "repeated layout entry key: " << type;
162  } else {
163  auto id = entry.getKey().get<StringAttr>();
164  if (!ids.insert(id).second)
165  return emitError() << "repeated layout entry key: " << id.getValue();
166  }
167  }
168  return success();
169 }
170 
171 /// Given a list of old and a list of new entries, overwrites old entries with
172 /// new ones if they have matching keys, appends new entries to the old entry
173 /// list otherwise.
174 static void
177  unsigned oldEntriesSize = oldEntries.size();
178  for (DataLayoutEntryInterface entry : newEntries) {
179  // We expect a small (dozens) number of entries, so it is practically
180  // cheaper to iterate over the list linearly rather than to create an
181  // auxiliary hashmap to avoid duplication. Also note that we never need to
182  // check for duplicate keys the values that were added from `newEntries`.
183  bool replaced = false;
184  for (unsigned i = 0; i < oldEntriesSize; ++i) {
185  if (oldEntries[i].getKey() == entry.getKey()) {
186  oldEntries[i] = entry;
187  replaced = true;
188  break;
189  }
190  }
191  if (!replaced)
192  oldEntries.push_back(entry);
193  }
194 }
195 
196 /// Combines a data layout spec into the given lists of entries organized by
197 /// type class and identifier, overwriting them if necessary. Fails to combine
198 /// if the two entries with identical keys are not compatible.
199 static LogicalResult
200 combineOneSpec(DataLayoutSpecInterface spec,
203  // A missing spec should be fine.
204  if (!spec)
205  return success();
206 
207  DenseMap<TypeID, DataLayoutEntryList> newEntriesForType;
209  spec.bucketEntriesByType(newEntriesForType, newEntriesForID);
210 
211  // Try overwriting the old entries with the new ones.
212  for (auto &kvp : newEntriesForType) {
213  if (!entriesForType.count(kvp.first)) {
214  entriesForType[kvp.first] = std::move(kvp.second);
215  continue;
216  }
217 
218  Type typeSample = kvp.second.front().getKey().get<Type>();
219  assert(&typeSample.getDialect() !=
220  typeSample.getContext()->getLoadedDialect<BuiltinDialect>() &&
221  "unexpected data layout entry for built-in type");
222 
223  auto interface = llvm::cast<DataLayoutTypeInterface>(typeSample);
224  if (!interface.areCompatible(entriesForType.lookup(kvp.first), kvp.second))
225  return failure();
226 
227  overwriteDuplicateEntries(entriesForType[kvp.first], kvp.second);
228  }
229 
230  for (const auto &kvp : newEntriesForID) {
231  StringAttr id = kvp.second.getKey().get<StringAttr>();
232  Dialect *dialect = id.getReferencedDialect();
233  if (!entriesForID.count(id)) {
234  entriesForID[id] = kvp.second;
235  continue;
236  }
237 
238  // Attempt to combine the enties using the dialect interface. If the
239  // dialect is not loaded for some reason, use the default combinator
240  // that conservatively accepts identical entries only.
241  entriesForID[id] =
242  dialect ? cast<DataLayoutDialectInterface>(dialect)->combine(
243  entriesForID[id], kvp.second)
245  kvp.second);
246  if (!entriesForID[id])
247  return failure();
248  }
249 
250  return success();
251 }
252 
255  // Only combine with attributes of the same kind.
256  // TODO: reconsider this when the need arises.
257  if (llvm::any_of(specs, [](DataLayoutSpecInterface spec) {
258  return !llvm::isa<DataLayoutSpecAttr>(spec);
259  }))
260  return {};
261 
262  // Combine all specs in order, with `this` being the last one.
265  for (DataLayoutSpecInterface spec : specs)
266  if (failed(combineOneSpec(spec, entriesForType, entriesForID)))
267  return nullptr;
268  if (failed(combineOneSpec(*this, entriesForType, entriesForID)))
269  return nullptr;
270 
271  // Rebuild the linear list of entries.
273  llvm::append_range(entries, llvm::make_second_range(entriesForID));
274  for (const auto &kvp : entriesForType)
275  llvm::append_range(entries, kvp.getSecond());
276 
277  return DataLayoutSpecAttr::get(getContext(), entries);
278 }
279 
281  return getImpl()->entries;
282 }
283 
284 StringAttr
286  return Builder(context).getStringAttr(DLTIDialect::kDataLayoutEndiannessKey);
287 }
288 
289 StringAttr
291  return Builder(context).getStringAttr(
292  DLTIDialect::kDataLayoutAllocaMemorySpaceKey);
293 }
294 
296  MLIRContext *context) const {
297  return Builder(context).getStringAttr(
298  DLTIDialect::kDataLayoutProgramMemorySpaceKey);
299 }
300 
301 StringAttr
303  return Builder(context).getStringAttr(
304  DLTIDialect::kDataLayoutGlobalMemorySpaceKey);
305 }
306 StringAttr
308  return Builder(context).getStringAttr(
309  DLTIDialect::kDataLayoutStackAlignmentKey);
310 }
311 
312 /// Parses an attribute with syntax
313 /// attr ::= `#target.` `dl_spec` `<` attr-list? `>`
314 /// attr-list ::= attr
315 /// | attr `,` attr-list
317  if (failed(parser.parseLess()))
318  return {};
319 
320  // Empty spec.
321  if (succeeded(parser.parseOptionalGreater()))
322  return get(parser.getContext(), {});
323 
325  if (parser.parseCommaSeparatedList(
326  [&]() { return parser.parseAttribute(entries.emplace_back()); }) ||
327  parser.parseGreater())
328  return {};
329 
330  return getChecked([&] { return parser.emitError(parser.getNameLoc()); },
331  parser.getContext(), entries);
332 }
333 
336  llvm::interleaveComma(getEntries(), os);
337  os << ">";
338 }
339 
340 //===----------------------------------------------------------------------===//
341 // DLTIDialect
342 //===----------------------------------------------------------------------===//
343 
344 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutAttrName;
345 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessKey;
346 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessBig;
347 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessLittle;
348 
349 namespace {
350 class TargetDataLayoutInterface : public DataLayoutDialectInterface {
351 public:
353 
354  LogicalResult verifyEntry(DataLayoutEntryInterface entry,
355  Location loc) const final {
356  StringRef entryName = entry.getKey().get<StringAttr>().strref();
357  if (entryName == DLTIDialect::kDataLayoutEndiannessKey) {
358  auto value = llvm::dyn_cast<StringAttr>(entry.getValue());
359  if (value &&
360  (value.getValue() == DLTIDialect::kDataLayoutEndiannessBig ||
361  value.getValue() == DLTIDialect::kDataLayoutEndiannessLittle))
362  return success();
363  return emitError(loc) << "'" << entryName
364  << "' data layout entry is expected to be either '"
365  << DLTIDialect::kDataLayoutEndiannessBig << "' or '"
366  << DLTIDialect::kDataLayoutEndiannessLittle << "'";
367  }
368  if (entryName == DLTIDialect::kDataLayoutAllocaMemorySpaceKey ||
369  entryName == DLTIDialect::kDataLayoutProgramMemorySpaceKey ||
370  entryName == DLTIDialect::kDataLayoutGlobalMemorySpaceKey ||
371  entryName == DLTIDialect::kDataLayoutStackAlignmentKey)
372  return success();
373  return emitError(loc) << "unknown data layout entry name: " << entryName;
374  }
375 };
376 } // namespace
377 
378 void DLTIDialect::initialize() {
379  addAttributes<DataLayoutEntryAttr, DataLayoutSpecAttr>();
380  addInterfaces<TargetDataLayoutInterface>();
381 }
382 
384  Type type) const {
385  StringRef attrKind;
386  if (parser.parseKeyword(&attrKind))
387  return {};
388 
389  if (attrKind == DataLayoutEntryAttr::kAttrKeyword)
390  return DataLayoutEntryAttr::parse(parser);
391  if (attrKind == DataLayoutSpecAttr::kAttrKeyword)
392  return DataLayoutSpecAttr::parse(parser);
393 
394  parser.emitError(parser.getNameLoc(), "unknown attrribute type: ")
395  << attrKind;
396  return {};
397 }
398 
399 void DLTIDialect::printAttribute(Attribute attr, DialectAsmPrinter &os) const {
402  [&](auto a) { a.print(os); })
403  .Default([](Attribute) { llvm_unreachable("unknown attribute kind"); });
404 }
405 
406 LogicalResult DLTIDialect::verifyOperationAttribute(Operation *op,
407  NamedAttribute attr) {
408  if (attr.getName() == DLTIDialect::kDataLayoutAttrName) {
409  if (!llvm::isa<DataLayoutSpecAttr>(attr.getValue())) {
410  return op->emitError() << "'" << DLTIDialect::kDataLayoutAttrName
411  << "' is expected to be a #dlti.dl_spec attribute";
412  }
413  if (isa<ModuleOp>(op))
414  return detail::verifyDataLayoutOp(op);
415  return success();
416  }
417 
418  return op->emitError() << "attribute '" << attr.getName().getValue()
419  << "' not supported by dialect";
420 }
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:200
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:175
static MLIRContext * getContext(OpFoldResult val)
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:72
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.
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 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.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
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:269
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...
A data layout entry attribute is a key-value pair where the key is a type or an identifier and the va...
Definition: DLTI.h:36
static DataLayoutEntryAttr parse(AsmParser &parser)
Parses an instance of this attribute.
Definition: DLTI.cpp:68
void print(AsmPrinter &os) const
Prints this attribute.
Definition: DLTI.cpp:95
constexpr static const llvm::StringLiteral kAttrKeyword
The keyword used for this attribute in custom syntax.
Definition: DLTI.h:41
DataLayoutEntryKey getKey() const
Returns the key of this entry.
Definition: DLTI.cpp:60
Attribute getValue() const
Returns the value of this entry.
Definition: DLTI.cpp:64
static DataLayoutEntryAttr get(StringAttr key, Attribute value)
Returns the entry with the given key and value.
Definition: DLTI.cpp:52
A data layout specification is a list of entries that specify (partial) data layout information.
Definition: DLTI.h:72
static DataLayoutSpecAttr get(MLIRContext *ctx, ArrayRef< DataLayoutEntryInterface > entries)
Returns the specification containing the given list of keys.
Definition: DLTI.cpp:141
StringAttr getProgramMemorySpaceIdentifier(MLIRContext *context) const
Returns the program memory space identifier.
Definition: DLTI.cpp:295
StringAttr getGlobalMemorySpaceIdentifier(MLIRContext *context) const
Returns the global memory space identifier.
Definition: DLTI.cpp:302
void print(AsmPrinter &os) const
Prints this attribute.
Definition: DLTI.cpp:334
StringAttr getStackAlignmentIdentifier(MLIRContext *context) const
Returns the stack alignment identifier.
Definition: DLTI.cpp:307
StringAttr getEndiannessIdentifier(MLIRContext *context) const
Returns the endiannes identifier.
Definition: DLTI.cpp:285
static LogicalResult verify(function_ref< InFlightDiagnostic()> emitError, ArrayRef< DataLayoutEntryInterface > entries)
Checks that the given list of entries does not contain duplicate keys.
Definition: DLTI.cpp:154
static DataLayoutSpecAttr parse(AsmParser &parser)
Parses an instance of this attribute.
Definition: DLTI.cpp:316
static DataLayoutSpecAttr getChecked(function_ref< InFlightDiagnostic()> emitError, MLIRContext *context, ArrayRef< DataLayoutEntryInterface > entries)
Returns the specification containing the given list of keys.
Definition: DLTI.cpp:147
StringAttr getAllocaMemorySpaceIdentifier(MLIRContext *context) const
Returns the alloca memory space identifier.
Definition: DLTI.cpp:290
constexpr static const StringLiteral kAttrKeyword
The keyword used for this attribute in custom syntax.
Definition: DLTI.h:77
DataLayoutEntryListRef getEntries() const
Returns the list of entries.
Definition: DLTI.cpp:280
DataLayoutSpecAttr combineWith(ArrayRef< DataLayoutSpecInterface > specs) const
Combines this specification with specs, enclosing specifications listed from outermost to innermost.
Definition: DLTI.cpp:254
The DialectAsmParser has methods for interacting with the asm parser when parsing attributes and type...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition: Dialect.h:41
This class represents a diagnostic that is inflight and set to be reported.
Definition: Diagnostics.h:308
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:202
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:216
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.
ArrayRef< T > copyInto(ArrayRef< T > elements)
Copy the specified array of elements into memory managed by our bump pointer allocator.
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:118
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition: Types.cpp:35
ImplType * getImpl() const
Utility for easy access to the storage instance.
std::pair< DataLayoutEntryKey, Attribute > KeyTy
Definition: DLTI.cpp:31
DataLayoutEntryKey entryKey
Definition: DLTI.cpp:46
static DataLayoutEntryStorage * construct(AttributeStorageAllocator &allocator, const KeyTy &key)
Definition: DLTI.cpp:36
DataLayoutEntryStorage(DataLayoutEntryKey entryKey, Attribute value)
Definition: DLTI.cpp:33
bool operator==(const KeyTy &other) const
Definition: DLTI.cpp:42
DataLayoutSpecStorage(ArrayRef< DataLayoutEntryInterface > entries)
Definition: DLTI.cpp:124
ArrayRef< DataLayoutEntryInterface > entries
Definition: DLTI.cpp:135
bool operator==(const KeyTy &key) const
Definition: DLTI.cpp:127
static DataLayoutSpecStorage * construct(AttributeStorageAllocator &allocator, const KeyTy &key)
Definition: DLTI.cpp:129
LogicalResult verifyDataLayoutOp(Operation *op)
Verifies that the operation implementing the data layout interface, or a module operation,...
Include the generated interface declarations.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
bool succeeded(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a success value.
Definition: LogicalResult.h:68
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
Attribute parseAttribute(llvm::StringRef attrStr, MLIRContext *context, Type type={}, size_t *numRead=nullptr, bool isKnownNullTerminated=false)
This parses a single MLIR attribute to an MLIR context if it was valid.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26