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(
287  DLTIDialect::kDataLayoutAllocaMemorySpaceKey);
288 }
289 
291  MLIRContext *context) const {
292  return Builder(context).getStringAttr(
293  DLTIDialect::kDataLayoutProgramMemorySpaceKey);
294 }
295 
296 StringAttr
298  return Builder(context).getStringAttr(
299  DLTIDialect::kDataLayoutGlobalMemorySpaceKey);
300 }
301 StringAttr
303  return Builder(context).getStringAttr(
304  DLTIDialect::kDataLayoutStackAlignmentKey);
305 }
306 
307 /// Parses an attribute with syntax
308 /// attr ::= `#target.` `dl_spec` `<` attr-list? `>`
309 /// attr-list ::= attr
310 /// | attr `,` attr-list
312  if (failed(parser.parseLess()))
313  return {};
314 
315  // Empty spec.
316  if (succeeded(parser.parseOptionalGreater()))
317  return get(parser.getContext(), {});
318 
320  if (parser.parseCommaSeparatedList(
321  [&]() { return parser.parseAttribute(entries.emplace_back()); }) ||
322  parser.parseGreater())
323  return {};
324 
325  return getChecked([&] { return parser.emitError(parser.getNameLoc()); },
326  parser.getContext(), entries);
327 }
328 
331  llvm::interleaveComma(getEntries(), os);
332  os << ">";
333 }
334 
335 //===----------------------------------------------------------------------===//
336 // DLTIDialect
337 //===----------------------------------------------------------------------===//
338 
339 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutAttrName;
340 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessKey;
341 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessBig;
342 constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessLittle;
343 
344 namespace {
345 class TargetDataLayoutInterface : public DataLayoutDialectInterface {
346 public:
348 
349  LogicalResult verifyEntry(DataLayoutEntryInterface entry,
350  Location loc) const final {
351  StringRef entryName = entry.getKey().get<StringAttr>().strref();
352  if (entryName == DLTIDialect::kDataLayoutEndiannessKey) {
353  auto value = llvm::dyn_cast<StringAttr>(entry.getValue());
354  if (value &&
355  (value.getValue() == DLTIDialect::kDataLayoutEndiannessBig ||
356  value.getValue() == DLTIDialect::kDataLayoutEndiannessLittle))
357  return success();
358  return emitError(loc) << "'" << entryName
359  << "' data layout entry is expected to be either '"
360  << DLTIDialect::kDataLayoutEndiannessBig << "' or '"
361  << DLTIDialect::kDataLayoutEndiannessLittle << "'";
362  }
363  if (entryName == DLTIDialect::kDataLayoutAllocaMemorySpaceKey ||
364  entryName == DLTIDialect::kDataLayoutProgramMemorySpaceKey ||
365  entryName == DLTIDialect::kDataLayoutGlobalMemorySpaceKey ||
366  entryName == DLTIDialect::kDataLayoutStackAlignmentKey)
367  return success();
368  return emitError(loc) << "unknown data layout entry name: " << entryName;
369  }
370 };
371 } // namespace
372 
373 void DLTIDialect::initialize() {
374  addAttributes<DataLayoutEntryAttr, DataLayoutSpecAttr>();
375  addInterfaces<TargetDataLayoutInterface>();
376 }
377 
379  Type type) const {
380  StringRef attrKind;
381  if (parser.parseKeyword(&attrKind))
382  return {};
383 
384  if (attrKind == DataLayoutEntryAttr::kAttrKeyword)
385  return DataLayoutEntryAttr::parse(parser);
386  if (attrKind == DataLayoutSpecAttr::kAttrKeyword)
387  return DataLayoutSpecAttr::parse(parser);
388 
389  parser.emitError(parser.getNameLoc(), "unknown attrribute type: ")
390  << attrKind;
391  return {};
392 }
393 
394 void DLTIDialect::printAttribute(Attribute attr, DialectAsmPrinter &os) const {
397  [&](auto a) { a.print(os); })
398  .Default([](Attribute) { llvm_unreachable("unknown attribute kind"); });
399 }
400 
401 LogicalResult DLTIDialect::verifyOperationAttribute(Operation *op,
402  NamedAttribute attr) {
403  if (attr.getName() == DLTIDialect::kDataLayoutAttrName) {
404  if (!llvm::isa<DataLayoutSpecAttr>(attr.getValue())) {
405  return op->emitError() << "'" << DLTIDialect::kDataLayoutAttrName
406  << "' is expected to be a #dlti.dl_spec attribute";
407  }
408  if (isa<ModuleOp>(op))
409  return detail::verifyDataLayoutOp(op);
410  return success();
411  }
412 
413  return op->emitError() << "attribute '" << attr.getName().getValue()
414  << "' not supported by dialect";
415 }
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:290
StringAttr getGlobalMemorySpaceIdentifier(MLIRContext *context) const
Returns the global memory space identifier.
Definition: DLTI.cpp:297
void print(AsmPrinter &os) const
Prints this attribute.
Definition: DLTI.cpp:329
StringAttr getStackAlignmentIdentifier(MLIRContext *context) const
Returns the stack alignment identifier.
Definition: DLTI.cpp:302
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:311
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:285
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