MLIR  22.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"
22 #include "llvm/ADT/APFloat.h"
23 #include "llvm/Support/Allocator.h"
24 #include <mutex>
25 
26 namespace mlir {
27 namespace detail {
28 
29 //===----------------------------------------------------------------------===//
30 // Elements Attributes
31 //===----------------------------------------------------------------------===//
32 
33 /// Return the bit width which DenseElementsAttr should use for this type.
34 inline size_t getDenseElementBitWidth(Type eltType) {
35  // Align the width for complex to 8 to make storage and interpretation easier.
36  if (ComplexType comp = llvm::dyn_cast<ComplexType>(eltType))
37  return llvm::alignTo<8>(getDenseElementBitWidth(comp.getElementType())) * 2;
38  if (eltType.isIndex())
39  return IndexType::kInternalStorageBitWidth;
40  return eltType.getIntOrFloatBitWidth();
41 }
42 
43 /// An attribute representing a reference to a dense vector or tensor object.
45 public:
47  : type(type), isSplat(isSplat) {}
48 
49  ShapedType type;
50  bool isSplat;
51 };
52 
53 /// An attribute representing a reference to a dense vector or tensor object.
56  bool isSplat = false)
58 
59  struct KeyTy {
60  KeyTy(ShapedType type, ArrayRef<char> data, llvm::hash_code hashCode,
61  bool isSplat = false)
63 
64  /// The type of the dense elements.
65  ShapedType type;
66 
67  /// The raw buffer for the data storage.
69 
70  /// The computed hash code for the storage data.
71  llvm::hash_code hashCode;
72 
73  /// A boolean that indicates if this data is a splat or not.
74  bool isSplat;
75  };
76 
77  /// Compare this storage instance with the provided key.
78  bool operator==(const KeyTy &key) const {
79  return key.type == type && key.data == data;
80  }
81 
82  /// Construct a key from a shaped type, raw data buffer, and a flag that
83  /// signals if the data is already known to be a splat. Callers to this
84  /// function are expected to tag preknown splat values when possible, e.g. one
85  /// element shapes.
86  static KeyTy getKey(ShapedType ty, ArrayRef<char> data, bool isKnownSplat) {
87  // Handle an empty storage instance.
88  if (data.empty())
89  return KeyTy(ty, data, 0);
90 
91  // If the data is already known to be a splat, the key hash value is
92  // directly the data buffer.
93  bool isBoolData = ty.getElementType().isInteger(1);
94  if (isKnownSplat) {
95  if (isBoolData)
96  return getKeyForSplatBoolData(ty, data[0] != 0);
97  return KeyTy(ty, data, llvm::hash_value(data), isKnownSplat);
98  }
99 
100  // Otherwise, we need to check if the data corresponds to a splat or not.
101 
102  // Handle the simple case of only one element.
103  size_t numElements = ty.getNumElements();
104  assert(numElements != 1 && "splat of 1 element should already be detected");
105 
106  // Handle boolean values directly as they are packed to 1-bit.
107  if (isBoolData)
108  return getKeyForBoolData(ty, data, numElements);
109 
110  size_t elementWidth = getDenseElementBitWidth(ty.getElementType());
111  // Non 1-bit dense elements are padded to 8-bits.
112  size_t storageSize = llvm::divideCeil(elementWidth, CHAR_BIT);
113  assert(((data.size() / storageSize) == numElements) &&
114  "data does not hold expected number of elements");
115 
116  // Create the initial hash value with just the first element.
117  auto firstElt = data.take_front(storageSize);
118  auto hashVal = llvm::hash_value(firstElt);
119 
120  // Check to see if this storage represents a splat. If it doesn't then
121  // combine the hash for the data starting with the first non splat element.
122  for (size_t i = storageSize, e = data.size(); i != e; i += storageSize)
123  if (memcmp(data.data(), &data[i], storageSize))
124  return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
125 
126  // Otherwise, this is a splat so just return the hash of the first element.
127  return KeyTy(ty, firstElt, hashVal, /*isSplat=*/true);
128  }
129 
130  /// Construct a key with a set of boolean data.
132  size_t numElements) {
133  ArrayRef<char> splatData = data;
134  bool splatValue = splatData.front() & 1;
135 
136  // Check the simple case where the data matches the known splat value.
137  if (splatData == ArrayRef<char>(splatValue ? kSplatTrue : kSplatFalse))
138  return getKeyForSplatBoolData(ty, splatValue);
139 
140  // Handle the case where the potential splat value is 1 and the number of
141  // elements is non 8-bit aligned.
142  size_t numOddElements = numElements % CHAR_BIT;
143  if (splatValue && numOddElements != 0) {
144  // Check that all bits are set in the last value.
145  char lastElt = splatData.back();
146  if (lastElt != llvm::maskTrailingOnes<unsigned char>(numOddElements))
147  return KeyTy(ty, data, llvm::hash_value(data));
148 
149  // If this is the only element, the data is known to be a splat.
150  if (splatData.size() == 1)
151  return getKeyForSplatBoolData(ty, splatValue);
152  splatData = splatData.drop_back();
153  }
154 
155  // Check that the data buffer corresponds to a splat of the proper mask.
156  char mask = splatValue ? ~0 : 0;
157  return llvm::all_of(splatData, [mask](char c) { return c == mask; })
158  ? getKeyForSplatBoolData(ty, splatValue)
159  : KeyTy(ty, data, llvm::hash_value(data));
160  }
161 
162  /// Return a key to use for a boolean splat of the given value.
163  static KeyTy getKeyForSplatBoolData(ShapedType type, bool splatValue) {
164  const char &splatData = splatValue ? kSplatTrue : kSplatFalse;
165  return KeyTy(type, splatData, llvm::hash_value(splatData),
166  /*isSplat=*/true);
167  }
168 
169  /// Hash the key for the storage.
170  static llvm::hash_code hashKey(const KeyTy &key) {
171  return llvm::hash_combine(key.type, key.hashCode);
172  }
173 
174  /// Construct a new storage instance.
177  // If the data buffer is non-empty, we copy it into the allocator with a
178  // 64-bit alignment.
179  ArrayRef<char> copy, data = key.data;
180  if (!data.empty()) {
181  char *rawData = reinterpret_cast<char *>(
182  allocator.allocate(data.size(), alignof(uint64_t)));
183  std::memcpy(rawData, data.data(), data.size());
184  copy = ArrayRef<char>(rawData, data.size());
185  }
186 
187  return new (allocator.allocate<DenseIntOrFPElementsAttrStorage>())
189  }
190 
192 
193  /// The values used to denote a boolean splat value.
194  // This is not using constexpr declaration due to compilation failure
195  // encountered with MSVC where it would inline these values, which makes it
196  // unsafe to refer by reference in KeyTy.
197  static const char kSplatTrue;
198  static const char kSplatFalse;
199 };
200 
201 /// An attribute representing a reference to a dense vector or tensor object
202 /// containing strings.
205  bool isSplat = false)
207 
208  struct KeyTy {
209  KeyTy(ShapedType type, ArrayRef<StringRef> data, llvm::hash_code hashCode,
210  bool isSplat = false)
212 
213  /// The type of the dense elements.
214  ShapedType type;
215 
216  /// The raw buffer for the data storage.
218 
219  /// The computed hash code for the storage data.
220  llvm::hash_code hashCode;
221 
222  /// A boolean that indicates if this data is a splat or not.
223  bool isSplat;
224  };
225 
226  /// Compare this storage instance with the provided key.
227  bool operator==(const KeyTy &key) const {
228  if (key.type != type)
229  return false;
230 
231  // Otherwise, we can default to just checking the data. StringRefs compare
232  // by contents.
233  return key.data == data;
234  }
235 
236  /// Construct a key from a shaped type, StringRef data buffer, and a flag that
237  /// signals if the data is already known to be a splat. Callers to this
238  /// function are expected to tag preknown splat values when possible, e.g. one
239  /// element shapes.
240  static KeyTy getKey(ShapedType ty, ArrayRef<StringRef> data,
241  bool isKnownSplat) {
242  // Handle an empty storage instance.
243  if (data.empty())
244  return KeyTy(ty, data, 0);
245 
246  // If the data is already known to be a splat, the key hash value is
247  // directly the data buffer.
248  if (isKnownSplat)
249  return KeyTy(ty, data, llvm::hash_value(data.front()), isKnownSplat);
250 
251  // Handle the simple case of only one element.
252  assert(ty.getNumElements() != 1 &&
253  "splat of 1 element should already be detected");
254 
255  // Create the initial hash value with just the first element.
256  const auto &firstElt = data.front();
257  auto hashVal = llvm::hash_value(firstElt);
258 
259  // Check to see if this storage represents a splat. If it doesn't then
260  // combine the hash for the data starting with the first non splat element.
261  for (size_t i = 1, e = data.size(); i != e; i++)
262  if (firstElt != data[i])
263  return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
264 
265  // Otherwise, this is a splat so just return the hash of the first element.
266  return KeyTy(ty, data.take_front(), hashVal, /*isSplat=*/true);
267  }
268 
269  /// Hash the key for the storage.
270  static llvm::hash_code hashKey(const KeyTy &key) {
271  return llvm::hash_combine(key.type, key.hashCode);
272  }
273 
274  /// Construct a new storage instance.
277  // If the data buffer is non-empty, we copy it into the allocator with a
278  // 64-bit alignment.
280  if (data.empty()) {
281  return new (allocator.allocate<DenseStringElementsAttrStorage>())
283  }
284 
285  int numEntries = key.isSplat ? 1 : data.size();
286 
287  // Compute the amount data needed to store the ArrayRef and StringRef
288  // contents.
289  size_t dataSize = sizeof(StringRef) * numEntries;
290  for (int i = 0; i < numEntries; i++)
291  dataSize += data[i].size();
292 
293  char *rawData = reinterpret_cast<char *>(
294  allocator.allocate(dataSize, alignof(uint64_t)));
295 
296  // Setup a mutable array ref of our string refs so that we can update their
297  // contents.
298  auto mutableCopy = MutableArrayRef<StringRef>(
299  reinterpret_cast<StringRef *>(rawData), numEntries);
300  auto *stringData = rawData + numEntries * sizeof(StringRef);
301 
302  for (int i = 0; i < numEntries; i++) {
303  memcpy(stringData, data[i].data(), data[i].size());
304  mutableCopy[i] = StringRef(stringData, data[i].size());
305  stringData += data[i].size();
306  }
307 
308  copy =
309  ArrayRef<StringRef>(reinterpret_cast<StringRef *>(rawData), numEntries);
310 
311  return new (allocator.allocate<DenseStringElementsAttrStorage>())
313  }
314 
316 };
317 
318 //===----------------------------------------------------------------------===//
319 // StringAttr
320 //===----------------------------------------------------------------------===//
321 
324  : type(type), value(value), referencedDialect(nullptr) {}
325 
326  /// The hash key is a tuple of the parameter types.
327  using KeyTy = std::pair<StringRef, Type>;
328  bool operator==(const KeyTy &key) const {
329  return value == key.first && type == key.second;
330  }
331  static ::llvm::hash_code hashKey(const KeyTy &key) {
333  }
334 
335  /// Define a construction method for creating a new instance of this
336  /// storage.
338  const KeyTy &key) {
339  return new (allocator.allocate<StringAttrStorage>())
340  StringAttrStorage(allocator.copyInto(key.first), key.second);
341  }
342 
343  /// Initialize the storage given an MLIRContext.
344  void initialize(MLIRContext *context);
345 
346  /// The type of the string.
348  /// The raw string value.
349  StringRef value;
350  /// If the string value contains a dialect namespace prefix (e.g.
351  /// dialect.blah), this is the dialect referenced.
353 };
354 
355 //===----------------------------------------------------------------------===//
356 // DistinctAttr
357 //===----------------------------------------------------------------------===//
358 
359 /// An attribute to store a distinct reference to another attribute.
361  using KeyTy = Attribute;
362 
365 
366  /// Returns the referenced attribute as key.
367  KeyTy getAsKey() const { return KeyTy(referencedAttr); }
368 
369  /// The referenced attribute.
371 };
372 
373 /// A specialized attribute uniquer for distinct attributes that always
374 /// allocates since the distinct attribute instances use the address of their
375 /// storage as unique identifier.
377 public:
378  /// Creates a distinct attribute storage. Allocates every time since the
379  /// address of the storage serves as unique identifier.
380  template <typename T, typename... Args>
381  static T get(MLIRContext *context, Args &&...args) {
382  static_assert(std::is_same_v<typename T::ImplType, DistinctAttrStorage>,
383  "expects a distinct attribute storage");
384  DistinctAttrStorage *storage = DistinctAttributeUniquer::allocateStorage(
385  context, std::forward<Args>(args)...);
388  return storage;
389  }
390 
391 private:
392  /// Allocates a distinct attribute storage.
393  static DistinctAttrStorage *allocateStorage(MLIRContext *context,
394  Attribute referencedAttr);
395 };
396 
397 /// An allocator for distinct attribute storage instances. Uses a synchronized
398 /// BumpPtrAllocator to ensure thread-safety. The allocated storage is deleted
399 /// when the DistinctAttributeAllocator is destroyed.
401 public:
407 
409  std::scoped_lock<std::mutex> guard(allocatorMutex);
410  return new (allocator.Allocate<DistinctAttrStorage>())
411  DistinctAttrStorage(referencedAttr);
412  };
413 
414 private:
415  /// Used to allocate distict attribute storages. The managed memory is freed
416  /// automatically when the allocator instance is destroyed.
417  llvm::BumpPtrAllocator allocator;
418 
419  /// Used to lock access to the allocator.
420  std::mutex allocatorMutex;
421 };
422 } // namespace detail
423 } // namespace mlir
424 
425 #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:63
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
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)
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 ...
llvm::hash_code hash_value(const StructType::MemberDecorationInfo &memberDecorationInfo)
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.