MLIR  21.0.0git
AttributeDetail.h
Go to the documentation of this file.
1 //===- AttributeDetail.h - MLIR Affine Map details Class --------*- C++ -*-===//
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 //
9 // This holds implementation details of Attribute.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef ATTRIBUTEDETAIL_H_
14 #define ATTRIBUTEDETAIL_H_
15 
16 #include "mlir/IR/AffineMap.h"
19 #include "mlir/IR/BuiltinTypes.h"
20 #include "mlir/IR/IntegerSet.h"
21 #include "mlir/IR/MLIRContext.h"
24 #include "llvm/ADT/APFloat.h"
25 #include "llvm/ADT/PointerIntPair.h"
26 #include "llvm/Support/TrailingObjects.h"
27 #include <mutex>
28 
29 namespace mlir {
30 namespace detail {
31 
32 //===----------------------------------------------------------------------===//
33 // Elements Attributes
34 //===----------------------------------------------------------------------===//
35 
36 /// Return the bit width which DenseElementsAttr should use for this type.
37 inline size_t getDenseElementBitWidth(Type eltType) {
38  // Align the width for complex to 8 to make storage and interpretation easier.
39  if (ComplexType comp = llvm::dyn_cast<ComplexType>(eltType))
40  return llvm::alignTo<8>(getDenseElementBitWidth(comp.getElementType())) * 2;
41  if (eltType.isIndex())
42  return IndexType::kInternalStorageBitWidth;
43  return eltType.getIntOrFloatBitWidth();
44 }
45 
46 /// An attribute representing a reference to a dense vector or tensor object.
48 public:
50  : type(type), isSplat(isSplat) {}
51 
52  ShapedType type;
53  bool isSplat;
54 };
55 
56 /// An attribute representing a reference to a dense vector or tensor object.
59  bool isSplat = false)
61 
62  struct KeyTy {
63  KeyTy(ShapedType type, ArrayRef<char> data, llvm::hash_code hashCode,
64  bool isSplat = false)
66 
67  /// The type of the dense elements.
68  ShapedType type;
69 
70  /// The raw buffer for the data storage.
72 
73  /// The computed hash code for the storage data.
74  llvm::hash_code hashCode;
75 
76  /// A boolean that indicates if this data is a splat or not.
77  bool isSplat;
78  };
79 
80  /// Compare this storage instance with the provided key.
81  bool operator==(const KeyTy &key) const {
82  return key.type == type && key.data == data;
83  }
84 
85  /// Construct a key from a shaped type, raw data buffer, and a flag that
86  /// signals if the data is already known to be a splat. Callers to this
87  /// function are expected to tag preknown splat values when possible, e.g. one
88  /// element shapes.
89  static KeyTy getKey(ShapedType ty, ArrayRef<char> data, bool isKnownSplat) {
90  // Handle an empty storage instance.
91  if (data.empty())
92  return KeyTy(ty, data, 0);
93 
94  // If the data is already known to be a splat, the key hash value is
95  // directly the data buffer.
96  bool isBoolData = ty.getElementType().isInteger(1);
97  if (isKnownSplat) {
98  if (isBoolData)
99  return getKeyForSplatBoolData(ty, data[0] != 0);
100  return KeyTy(ty, data, llvm::hash_value(data), isKnownSplat);
101  }
102 
103  // Otherwise, we need to check if the data corresponds to a splat or not.
104 
105  // Handle the simple case of only one element.
106  size_t numElements = ty.getNumElements();
107  assert(numElements != 1 && "splat of 1 element should already be detected");
108 
109  // Handle boolean values directly as they are packed to 1-bit.
110  if (isBoolData)
111  return getKeyForBoolData(ty, data, numElements);
112 
113  size_t elementWidth = getDenseElementBitWidth(ty.getElementType());
114  // Non 1-bit dense elements are padded to 8-bits.
115  size_t storageSize = llvm::divideCeil(elementWidth, CHAR_BIT);
116  assert(((data.size() / storageSize) == numElements) &&
117  "data does not hold expected number of elements");
118 
119  // Create the initial hash value with just the first element.
120  auto firstElt = data.take_front(storageSize);
121  auto hashVal = llvm::hash_value(firstElt);
122 
123  // Check to see if this storage represents a splat. If it doesn't then
124  // combine the hash for the data starting with the first non splat element.
125  for (size_t i = storageSize, e = data.size(); i != e; i += storageSize)
126  if (memcmp(data.data(), &data[i], storageSize))
127  return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
128 
129  // Otherwise, this is a splat so just return the hash of the first element.
130  return KeyTy(ty, firstElt, hashVal, /*isSplat=*/true);
131  }
132 
133  /// Construct a key with a set of boolean data.
135  size_t numElements) {
136  ArrayRef<char> splatData = data;
137  bool splatValue = splatData.front() & 1;
138 
139  // Check the simple case where the data matches the known splat value.
140  if (splatData == ArrayRef<char>(splatValue ? kSplatTrue : kSplatFalse))
141  return getKeyForSplatBoolData(ty, splatValue);
142 
143  // Handle the case where the potential splat value is 1 and the number of
144  // elements is non 8-bit aligned.
145  size_t numOddElements = numElements % CHAR_BIT;
146  if (splatValue && numOddElements != 0) {
147  // Check that all bits are set in the last value.
148  char lastElt = splatData.back();
149  if (lastElt != llvm::maskTrailingOnes<unsigned char>(numOddElements))
150  return KeyTy(ty, data, llvm::hash_value(data));
151 
152  // If this is the only element, the data is known to be a splat.
153  if (splatData.size() == 1)
154  return getKeyForSplatBoolData(ty, splatValue);
155  splatData = splatData.drop_back();
156  }
157 
158  // Check that the data buffer corresponds to a splat of the proper mask.
159  char mask = splatValue ? ~0 : 0;
160  return llvm::all_of(splatData, [mask](char c) { return c == mask; })
161  ? getKeyForSplatBoolData(ty, splatValue)
162  : KeyTy(ty, data, llvm::hash_value(data));
163  }
164 
165  /// Return a key to use for a boolean splat of the given value.
166  static KeyTy getKeyForSplatBoolData(ShapedType type, bool splatValue) {
167  const char &splatData = splatValue ? kSplatTrue : kSplatFalse;
168  return KeyTy(type, splatData, llvm::hash_value(splatData),
169  /*isSplat=*/true);
170  }
171 
172  /// Hash the key for the storage.
173  static llvm::hash_code hashKey(const KeyTy &key) {
174  return llvm::hash_combine(key.type, key.hashCode);
175  }
176 
177  /// Construct a new storage instance.
180  // If the data buffer is non-empty, we copy it into the allocator with a
181  // 64-bit alignment.
182  ArrayRef<char> copy, data = key.data;
183  if (!data.empty()) {
184  char *rawData = reinterpret_cast<char *>(
185  allocator.allocate(data.size(), alignof(uint64_t)));
186  std::memcpy(rawData, data.data(), data.size());
187  copy = ArrayRef<char>(rawData, data.size());
188  }
189 
190  return new (allocator.allocate<DenseIntOrFPElementsAttrStorage>())
192  }
193 
195 
196  /// The values used to denote a boolean splat value.
197  // This is not using constexpr declaration due to compilation failure
198  // encountered with MSVC where it would inline these values, which makes it
199  // unsafe to refer by reference in KeyTy.
200  static const char kSplatTrue;
201  static const char kSplatFalse;
202 };
203 
204 /// An attribute representing a reference to a dense vector or tensor object
205 /// containing strings.
208  bool isSplat = false)
210 
211  struct KeyTy {
212  KeyTy(ShapedType type, ArrayRef<StringRef> data, llvm::hash_code hashCode,
213  bool isSplat = false)
215 
216  /// The type of the dense elements.
217  ShapedType type;
218 
219  /// The raw buffer for the data storage.
221 
222  /// The computed hash code for the storage data.
223  llvm::hash_code hashCode;
224 
225  /// A boolean that indicates if this data is a splat or not.
226  bool isSplat;
227  };
228 
229  /// Compare this storage instance with the provided key.
230  bool operator==(const KeyTy &key) const {
231  if (key.type != type)
232  return false;
233 
234  // Otherwise, we can default to just checking the data. StringRefs compare
235  // by contents.
236  return key.data == data;
237  }
238 
239  /// Construct a key from a shaped type, StringRef data buffer, and a flag that
240  /// signals if the data is already known to be a splat. Callers to this
241  /// function are expected to tag preknown splat values when possible, e.g. one
242  /// element shapes.
243  static KeyTy getKey(ShapedType ty, ArrayRef<StringRef> data,
244  bool isKnownSplat) {
245  // Handle an empty storage instance.
246  if (data.empty())
247  return KeyTy(ty, data, 0);
248 
249  // If the data is already known to be a splat, the key hash value is
250  // directly the data buffer.
251  if (isKnownSplat)
252  return KeyTy(ty, data, llvm::hash_value(data.front()), isKnownSplat);
253 
254  // Handle the simple case of only one element.
255  assert(ty.getNumElements() != 1 &&
256  "splat of 1 element should already be detected");
257 
258  // Create the initial hash value with just the first element.
259  const auto &firstElt = data.front();
260  auto hashVal = llvm::hash_value(firstElt);
261 
262  // Check to see if this storage represents a splat. If it doesn't then
263  // combine the hash for the data starting with the first non splat element.
264  for (size_t i = 1, e = data.size(); i != e; i++)
265  if (firstElt != data[i])
266  return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
267 
268  // Otherwise, this is a splat so just return the hash of the first element.
269  return KeyTy(ty, data.take_front(), hashVal, /*isSplat=*/true);
270  }
271 
272  /// Hash the key for the storage.
273  static llvm::hash_code hashKey(const KeyTy &key) {
274  return llvm::hash_combine(key.type, key.hashCode);
275  }
276 
277  /// Construct a new storage instance.
280  // If the data buffer is non-empty, we copy it into the allocator with a
281  // 64-bit alignment.
283  if (data.empty()) {
284  return new (allocator.allocate<DenseStringElementsAttrStorage>())
286  }
287 
288  int numEntries = key.isSplat ? 1 : data.size();
289 
290  // Compute the amount data needed to store the ArrayRef and StringRef
291  // contents.
292  size_t dataSize = sizeof(StringRef) * numEntries;
293  for (int i = 0; i < numEntries; i++)
294  dataSize += data[i].size();
295 
296  char *rawData = reinterpret_cast<char *>(
297  allocator.allocate(dataSize, alignof(uint64_t)));
298 
299  // Setup a mutable array ref of our string refs so that we can update their
300  // contents.
301  auto mutableCopy = MutableArrayRef<StringRef>(
302  reinterpret_cast<StringRef *>(rawData), numEntries);
303  auto *stringData = rawData + numEntries * sizeof(StringRef);
304 
305  for (int i = 0; i < numEntries; i++) {
306  memcpy(stringData, data[i].data(), data[i].size());
307  mutableCopy[i] = StringRef(stringData, data[i].size());
308  stringData += data[i].size();
309  }
310 
311  copy =
312  ArrayRef<StringRef>(reinterpret_cast<StringRef *>(rawData), numEntries);
313 
314  return new (allocator.allocate<DenseStringElementsAttrStorage>())
316  }
317 
319 };
320 
321 //===----------------------------------------------------------------------===//
322 // StringAttr
323 //===----------------------------------------------------------------------===//
324 
327  : type(type), value(value), referencedDialect(nullptr) {}
328 
329  /// The hash key is a tuple of the parameter types.
330  using KeyTy = std::pair<StringRef, Type>;
331  bool operator==(const KeyTy &key) const {
332  return value == key.first && type == key.second;
333  }
334  static ::llvm::hash_code hashKey(const KeyTy &key) {
336  }
337 
338  /// Define a construction method for creating a new instance of this
339  /// storage.
341  const KeyTy &key) {
342  return new (allocator.allocate<StringAttrStorage>())
343  StringAttrStorage(allocator.copyInto(key.first), key.second);
344  }
345 
346  /// Initialize the storage given an MLIRContext.
347  void initialize(MLIRContext *context);
348 
349  /// The type of the string.
351  /// The raw string value.
352  StringRef value;
353  /// If the string value contains a dialect namespace prefix (e.g.
354  /// dialect.blah), this is the dialect referenced.
356 };
357 
358 //===----------------------------------------------------------------------===//
359 // DistinctAttr
360 //===----------------------------------------------------------------------===//
361 
362 /// An attribute to store a distinct reference to another attribute.
364  using KeyTy = Attribute;
365 
368 
369  /// Returns the referenced attribute as key.
370  KeyTy getAsKey() const { return KeyTy(referencedAttr); }
371 
372  /// The referenced attribute.
374 };
375 
376 /// A specialized attribute uniquer for distinct attributes that always
377 /// allocates since the distinct attribute instances use the address of their
378 /// storage as unique identifier.
380 public:
381  /// Creates a distinct attribute storage. Allocates every time since the
382  /// address of the storage serves as unique identifier.
383  template <typename T, typename... Args>
384  static T get(MLIRContext *context, Args &&...args) {
385  static_assert(std::is_same_v<typename T::ImplType, DistinctAttrStorage>,
386  "expects a distinct attribute storage");
387  DistinctAttrStorage *storage = DistinctAttributeUniquer::allocateStorage(
388  context, std::forward<Args>(args)...);
391  return storage;
392  }
393 
394 private:
395  /// Allocates a distinct attribute storage.
396  static DistinctAttrStorage *allocateStorage(MLIRContext *context,
397  Attribute referencedAttr);
398 };
399 
400 /// An allocator for distinct attribute storage instances. It uses thread local
401 /// bump pointer allocators stored in a thread local cache to ensure the storage
402 /// is freed after the destruction of the distinct attribute allocator.
404 public:
405  DistinctAttributeAllocator(bool threadingIsEnabled)
406  : threadingIsEnabled(threadingIsEnabled), useThreadLocalAllocator(true) {};
407 
412 
413  /// Allocates a distinct attribute storage using a thread local bump pointer
414  /// allocator to enable synchronization free parallel allocations.
416  if (!useThreadLocalAllocator && threadingIsEnabled) {
417  std::scoped_lock<std::mutex> lock(allocatorMutex);
418  return allocateImpl(referencedAttr);
419  }
420  return allocateImpl(referencedAttr);
421  }
422 
423  /// Sets a flag that stores if multithreading is enabled. The flag is used to
424  /// decide if locking is needed when using a non thread-safe allocator.
425  void disableMultiThreading(bool disable = true) {
426  threadingIsEnabled = !disable;
427  }
428 
429  /// Sets a flag to disable using thread local bump pointer allocators and use
430  /// a single thread-safe allocator. Use this to persist allocated storage
431  /// beyond the lifetime of a child thread calling this function while ensuring
432  /// thread-safe allocation.
433  void disableThreadLocalStorage(bool disable = true) {
434  useThreadLocalAllocator = !disable;
435  }
436 
437 private:
438  DistinctAttrStorage *allocateImpl(Attribute referencedAttr) {
439  return new (getAllocatorInUse().Allocate<DistinctAttrStorage>())
440  DistinctAttrStorage(referencedAttr);
441  }
442 
443  /// If threading is disabled on the owning MLIR context, a normal non
444  /// thread-local, non-thread safe bump pointer allocator is used instead to
445  /// prevent use-after-free errors whenever attribute storage created on a
446  /// crash recover thread is accessed after the thread joins.
447  llvm::BumpPtrAllocator &getAllocatorInUse() {
448  if (useThreadLocalAllocator)
449  return allocatorCache.get();
450  return allocator;
451  }
452 
453  ThreadLocalCache<llvm::BumpPtrAllocator> allocatorCache;
454  llvm::BumpPtrAllocator allocator;
455  std::mutex allocatorMutex;
456 
457  bool threadingIsEnabled : 1;
458  bool useThreadLocalAllocator : 1;
459 };
460 } // namespace detail
461 } // namespace mlir
462 
463 #endif // ATTRIBUTEDETAIL_H_
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
static const AbstractAttribute & lookup(TypeID typeID, MLIRContext *context)
Look up the specified abstract attribute in the MLIRContext and return a reference to it.
Base storage class appearing in an attribute.
void initializeAbstractAttribute(const AbstractAttribute &abstractAttr)
Set the abstract attribute for this storage instance.
Attributes are known-constant values of operations.
Definition: Attributes.h:25
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition: Dialect.h:38
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
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.
ValueT & get()
Return an instance of the value type for the current thread.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
bool isIndex() const
Definition: Types.cpp:54
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition: Types.cpp:122
An allocator for distinct attribute storage instances.
DistinctAttributeAllocator(const DistinctAttributeAllocator &)=delete
DistinctAttributeAllocator & operator=(const DistinctAttributeAllocator &)=delete
DistinctAttributeAllocator(DistinctAttributeAllocator &&)=delete
DistinctAttrStorage * allocate(Attribute referencedAttr)
Allocates a distinct attribute storage using a thread local bump pointer allocator to enable synchron...
void disableMultiThreading(bool disable=true)
Sets a flag that stores if multithreading is enabled.
DistinctAttributeAllocator(bool threadingIsEnabled)
void disableThreadLocalStorage(bool disable=true)
Sets a flag to disable using thread local bump pointer allocators and use a single thread-safe alloca...
A specialized attribute uniquer for distinct attributes that always allocates since the distinct attr...
static T get(MLIRContext *context, Args &&...args)
Creates a distinct attribute storage.
size_t getDenseElementBitWidth(Type eltType)
Return the bit width which DenseElementsAttr should use for this type.
llvm::TypeSize divideCeil(llvm::TypeSize numerator, uint64_t denominator)
Divides the known min value of the numerator by the denominator and rounds the result up to the next ...
inline ::llvm::hash_code hash_value(const PolynomialBase< D, T > &arg)
Definition: Polynomial.h:262
Include the generated interface declarations.
An attribute representing a reference to a dense vector or tensor object.
DenseElementsAttributeStorage(ShapedType type, bool isSplat)
llvm::hash_code hashCode
The computed hash code for the storage data.
bool isSplat
A boolean that indicates if this data is a splat or not.
KeyTy(ShapedType type, ArrayRef< char > data, llvm::hash_code hashCode, bool isSplat=false)
ArrayRef< char > data
The raw buffer for the data storage.
ShapedType type
The type of the dense elements.
An attribute representing a reference to a dense vector or tensor object.
bool operator==(const KeyTy &key) const
Compare this storage instance with the provided key.
static KeyTy getKey(ShapedType ty, ArrayRef< char > data, bool isKnownSplat)
Construct a key from a shaped type, raw data buffer, and a flag that signals if the data is already k...
static const char kSplatTrue
The values used to denote a boolean splat value.
static llvm::hash_code hashKey(const KeyTy &key)
Hash the key for the storage.
static DenseIntOrFPElementsAttrStorage * construct(AttributeStorageAllocator &allocator, KeyTy key)
Construct a new storage instance.
DenseIntOrFPElementsAttrStorage(ShapedType ty, ArrayRef< char > data, bool isSplat=false)
static KeyTy getKeyForSplatBoolData(ShapedType type, bool splatValue)
Return a key to use for a boolean splat of the given value.
static KeyTy getKeyForBoolData(ShapedType ty, ArrayRef< char > data, size_t numElements)
Construct a key with a set of boolean data.
KeyTy(ShapedType type, ArrayRef< StringRef > data, llvm::hash_code hashCode, bool isSplat=false)
ShapedType type
The type of the dense elements.
bool isSplat
A boolean that indicates if this data is a splat or not.
ArrayRef< StringRef > data
The raw buffer for the data storage.
llvm::hash_code hashCode
The computed hash code for the storage data.
An attribute representing a reference to a dense vector or tensor object containing strings.
static DenseStringElementsAttrStorage * construct(AttributeStorageAllocator &allocator, KeyTy key)
Construct a new storage instance.
static KeyTy getKey(ShapedType ty, ArrayRef< StringRef > data, bool isKnownSplat)
Construct a key from a shaped type, StringRef data buffer, and a flag that signals if the data is alr...
bool operator==(const KeyTy &key) const
Compare this storage instance with the provided key.
DenseStringElementsAttrStorage(ShapedType ty, ArrayRef< StringRef > data, bool isSplat=false)
static llvm::hash_code hashKey(const KeyTy &key)
Hash the key for the storage.
An attribute to store a distinct reference to another attribute.
DistinctAttrStorage(Attribute referencedAttr)
Attribute referencedAttr
The referenced attribute.
KeyTy getAsKey() const
Returns the referenced attribute as key.
Type type
The type of the string.
StringRef value
The raw string value.
bool operator==(const KeyTy &key) const
::llvm::hash_code hashKey(const KeyTy &key)
static StringAttrStorage * construct(AttributeStorageAllocator &allocator, const KeyTy &key)
Define a construction method for creating a new instance of this storage.
StringAttrStorage(StringRef value, Type type)
Dialect * referencedDialect
If the string value contains a dialect namespace prefix (e.g.
std::pair< StringRef, Type > KeyTy
The hash key is a tuple of the parameter types.
void initialize(MLIRContext *context)
Initialize the storage given an MLIRContext.