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"
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
26namespace mlir {
27namespace detail {
28
29//===----------------------------------------------------------------------===//
30// Elements Attributes
31//===----------------------------------------------------------------------===//
32
33/// Return the bit width which DenseElementsAttr should use for this type.
34inline 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.
45public:
48
49 ShapedType type;
50 bool isSplat;
51};
52
53/// An attribute representing a reference to a dense vector or tensor object.
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.
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.
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.
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
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.
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.
377public:
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
391private:
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.
401public:
407
409 std::scoped_lock<std::mutex> guard(allocatorMutex);
410 return new (allocator.Allocate<DistinctAttrStorage>())
411 DistinctAttrStorage(referencedAttr);
412 };
413
414private:
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
TypeID getTypeID()
Return a unique identifier for the concrete attribute type.
Definition Attributes.h:52
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
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
DistinctAttrStorage * allocate(Attribute referencedAttr)
DistinctAttributeAllocator(const DistinctAttributeAllocator &)=delete
DistinctAttributeAllocator & operator=(const DistinctAttributeAllocator &)=delete
DistinctAttributeAllocator(DistinctAttributeAllocator &&)=delete
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.
AttrTypeReplacer.
size_t getDenseElementBitWidth(Type eltType)
Return the bit width which DenseElementsAttr should use for this type.
Include the generated interface declarations.
llvm::DenseMapInfo< T, Enable > DenseMapInfo
Definition LLVM.h:122
StorageUniquer::StorageAllocator AttributeStorageAllocator
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 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.
static DenseStringElementsAttrStorage * construct(AttributeStorageAllocator &allocator, KeyTy key)
Construct a new storage instance.
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)
StringAttrStorage(StringRef value, Type type)
static StringAttrStorage * construct(AttributeStorageAllocator &allocator, const KeyTy &key)
Define a construction method for creating a new instance of this storage.
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.