MLIR 24.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"
21#include "mlir/IR/IntegerSet.h"
22#include "mlir/IR/MLIRContext.h"
23#include "llvm/ADT/APFloat.h"
24#include "llvm/Support/Allocator.h"
25#include "llvm/Support/RWMutex.h"
26#include <mutex>
27
28namespace mlir {
29namespace detail {
30
31//===----------------------------------------------------------------------===//
32// Elements Attributes
33//===----------------------------------------------------------------------===//
34
35/// Return the bit width which DenseElementsAttr should use for this type.
36inline size_t getDenseElementBitWidth(Type eltType) {
37 if (auto denseEltType = llvm::dyn_cast<DenseElementType>(eltType))
38 return denseEltType.getDenseElementBitSize();
39 llvm_unreachable("unsupported element type");
40}
41
42/// An attribute representing a reference to a dense vector or tensor object.
44public:
46
47 ShapedType type;
48};
49
50/// An attribute representing a reference to a dense vector or tensor object.
54
55 struct KeyTy {
56 KeyTy(ShapedType type, ArrayRef<char> data, llvm::hash_code hashCode)
58
59 /// The type of the dense elements.
60 ShapedType type;
61
62 /// The raw buffer for the data storage.
64
65 /// The computed hash code for the storage data.
66 llvm::hash_code hashCode;
67 };
68
69 /// Compare this storage instance with the provided key.
70 bool operator==(const KeyTy &key) const {
71 return key.type == type && key.data == data;
72 }
73
74 /// Construct a key from a shaped type and raw data buffer.
75 static KeyTy getKey(ShapedType ty, ArrayRef<char> data) {
76 // Handle an empty storage instance.
77 if (data.empty())
78 return KeyTy(ty, data, 0);
79
80 size_t elementWidth = getDenseElementBitWidth(ty.getElementType());
81 // Dense elements are padded to 8-bits.
82 size_t storageSize = llvm::divideCeil(elementWidth, CHAR_BIT);
83
84 // If the data buffer holds a single element, it is a known splat.
85 if (data.size() == storageSize)
86 return KeyTy(ty, data, llvm::hash_value(data));
87
88 assert(((data.size() / storageSize) ==
89 static_cast<size_t>(ty.getNumElements())) &&
90 "data does not hold expected number of elements");
91
92 // Create the initial hash value with just the first element.
93 auto firstElt = data.take_front(storageSize);
94 auto hashVal = llvm::hash_value(firstElt);
95
96 // Check to see if this storage represents a splat. If it doesn't then
97 // combine the hash for the data starting with the first non splat element.
98 for (size_t i = storageSize, e = data.size(); i != e; i += storageSize)
99 if (memcmp(data.data(), &data[i], storageSize))
100 return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
101
102 // Otherwise, this is a splat so just return the hash of the first element.
103 return KeyTy(ty, firstElt, hashVal);
104 }
105
106 /// Hash the key for the storage.
107 static llvm::hash_code hashKey(const KeyTy &key) {
108 return llvm::hash_combine(key.type, key.hashCode);
109 }
110
111 /// Construct a new storage instance.
114 // If the data buffer is non-empty, we copy it into the allocator with a
115 // 64-bit alignment.
117 if (!data.empty()) {
118 char *rawData = reinterpret_cast<char *>(
119 allocator.allocate(data.size(), alignof(uint64_t)));
120 std::memcpy(rawData, data.data(), data.size());
121 copy = ArrayRef<char>(rawData, data.size());
122 }
123
124 return new (allocator.allocate<DenseTypedElementsAttrStorage>())
126 }
127
129};
130
131/// An attribute representing a reference to a dense vector or tensor object
132/// containing strings.
136
137 struct KeyTy {
138 KeyTy(ShapedType type, ArrayRef<StringRef> data, llvm::hash_code hashCode)
140
141 /// The type of the dense elements.
142 ShapedType type;
143
144 /// The raw buffer for the data storage.
146
147 /// The computed hash code for the storage data.
148 llvm::hash_code hashCode;
149 };
150
151 /// Compare this storage instance with the provided key.
152 bool operator==(const KeyTy &key) const {
153 if (key.type != type)
154 return false;
155
156 // Otherwise, we can default to just checking the data. StringRefs compare
157 // by contents.
158 return key.data == data;
159 }
160
161 /// Construct a key from a shaped type and StringRef data buffer.
162 static KeyTy getKey(ShapedType ty, ArrayRef<StringRef> data) {
163 // Handle an empty storage instance.
164 if (data.empty())
165 return KeyTy(ty, data, 0);
166
167 // If the data buffer holds a single element, it is a known splat.
168 if (data.size() == 1)
169 return KeyTy(ty, data, llvm::hash_value(data.front()));
170
171 // Create the initial hash value with just the first element.
172 const auto &firstElt = data.front();
173 auto hashVal = llvm::hash_value(firstElt);
174
175 // Check to see if this storage represents a splat. If it doesn't then
176 // combine the hash for the data starting with the first non splat element.
177 for (size_t i = 1, e = data.size(); i != e; ++i)
178 if (firstElt != data[i])
179 return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
180
181 // Otherwise, this is a splat so just return the hash of the first element.
182 return KeyTy(ty, data.take_front(), hashVal);
183 }
184
185 /// Hash the key for the storage.
186 static llvm::hash_code hashKey(const KeyTy &key) {
187 return llvm::hash_combine(key.type, key.hashCode);
188 }
189
190 /// Construct a new storage instance.
193 // If the data buffer is non-empty, we copy it into the allocator with a
194 // 64-bit alignment.
196 if (data.empty()) {
197 return new (allocator.allocate<DenseStringElementsAttrStorage>())
199 }
200
201 size_t numEntries = data.size();
202
203 // Compute the amount data needed to store the ArrayRef and StringRef
204 // contents.
205 size_t dataSize = sizeof(StringRef) * numEntries;
206 for (size_t i = 0; i < numEntries; ++i)
207 dataSize += data[i].size();
208
209 char *rawData = reinterpret_cast<char *>(
210 allocator.allocate(dataSize, alignof(uint64_t)));
211
212 // Setup a mutable array ref of our string refs so that we can update their
213 // contents.
214 auto mutableCopy = MutableArrayRef<StringRef>(
215 reinterpret_cast<StringRef *>(rawData), numEntries);
216 auto *stringData = rawData + numEntries * sizeof(StringRef);
217
218 for (size_t i = 0; i < numEntries; ++i) {
219 memcpy(stringData, data[i].data(), data[i].size());
220 mutableCopy[i] = StringRef(stringData, data[i].size());
221 stringData += data[i].size();
222 }
223
224 copy =
225 ArrayRef<StringRef>(reinterpret_cast<StringRef *>(rawData), numEntries);
226
227 return new (allocator.allocate<DenseStringElementsAttrStorage>())
229 }
230
232};
233
234//===----------------------------------------------------------------------===//
235// StringAttr
236//===----------------------------------------------------------------------===//
237
241
242 /// The hash key is a tuple of the parameter types.
243 using KeyTy = std::pair<StringRef, Type>;
244 bool operator==(const KeyTy &key) const {
245 return value == key.first && type == key.second;
246 }
247 static ::llvm::hash_code hashKey(const KeyTy &key) {
249 }
250
251 /// Define a construction method for creating a new instance of this
252 /// storage.
254 const KeyTy &key) {
255 return new (allocator.allocate<StringAttrStorage>())
256 StringAttrStorage(allocator.copyInto(key.first), key.second);
257 }
258
259 /// Initialize the storage given an MLIRContext.
260 void initialize(MLIRContext *context);
261
262 /// The type of the string.
264 /// The raw string value.
265 StringRef value;
266 /// If the string value contains a dialect namespace prefix (e.g.
267 /// dialect.blah), this is the dialect referenced.
269};
270
271//===----------------------------------------------------------------------===//
272// DistinctAttr
273//===----------------------------------------------------------------------===//
274
275/// An attribute to store a distinct reference to another attribute.
278
281
282 /// Returns the referenced attribute as key.
283 KeyTy getAsKey() const { return KeyTy(referencedAttr); }
284
285 /// The referenced attribute.
287};
288
289/// A specialized attribute uniquer for distinct attributes that always
290/// allocates since the distinct attribute instances use the address of their
291/// storage as unique identifier.
293public:
294 /// Creates a distinct attribute storage. Allocates every time since the
295 /// address of the storage serves as unique identifier.
296 template <typename T, typename... Args>
297 static T get(MLIRContext *context, Args &&...args) {
298 static_assert(std::is_same_v<typename T::ImplType, DistinctAttrStorage>,
299 "expects a distinct attribute storage");
300 DistinctAttrStorage *storage = DistinctAttributeUniquer::allocateStorage(
301 context, std::forward<Args>(args)...);
304 return storage;
305 }
306
307private:
308 /// Allocates a distinct attribute storage.
309 static DistinctAttrStorage *allocateStorage(MLIRContext *context,
310 Attribute referencedAttr);
311};
312
313/// An allocator for distinct attribute storage instances. Uses a synchronized
314/// BumpPtrAllocator to ensure thread-safety. The allocated storage is deleted
315/// when the DistinctAttributeAllocator is destroyed.
317public:
323
325 llvm::sys::SmartScopedWriter<true> guard(allocatorMutex);
326 llvm::BumpPtrAllocator &alloc =
327 transientAllocator ? *transientAllocator : allocator;
328 return new (alloc.Allocate<DistinctAttrStorage>())
329 DistinctAttrStorage(referencedAttr);
330 }
331
333 assert(!transientAllocator &&
334 "distinct attribute allocator is already in a transient scope");
335 transientAllocator = std::make_unique<llvm::BumpPtrAllocator>();
336 }
337
338 void endTransientScope() { transientAllocator.reset(); }
339
340 bool isInTransientScope() const { return transientAllocator != nullptr; }
341
342private:
343 /// Used to allocate distinct attribute storages in base layer.
344 llvm::BumpPtrAllocator allocator;
345
346 /// Used to allocate distinct attribute storages in transient layer.
347 std::unique_ptr<llvm::BumpPtrAllocator> transientAllocator;
348
349 /// Used to synchronize access to the allocator. Uses a RW mutex so that
350 /// isInTransientScope() can take a cheap reader lock.
351 mutable llvm::sys::SmartRWMutex<true> allocatorMutex;
352};
353} // namespace detail
354} // namespace mlir
355
356#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
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:116
StorageUniquer::StorageAllocator AttributeStorageAllocator
ShapedType type
The type of the dense elements.
ArrayRef< StringRef > data
The raw buffer for the data storage.
KeyTy(ShapedType type, ArrayRef< StringRef > data, llvm::hash_code hashCode)
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.
bool operator==(const KeyTy &key) const
Compare this storage instance with the provided key.
static KeyTy getKey(ShapedType ty, ArrayRef< StringRef > data)
Construct a key from a shaped type and StringRef data buffer.
DenseStringElementsAttrStorage(ShapedType ty, ArrayRef< StringRef > data)
static DenseStringElementsAttrStorage * construct(AttributeStorageAllocator &allocator, KeyTy key)
Construct a new storage instance.
static llvm::hash_code hashKey(const KeyTy &key)
Hash the key for the storage.
KeyTy(ShapedType type, ArrayRef< char > data, llvm::hash_code hashCode)
ArrayRef< char > data
The raw buffer for the data storage.
ShapedType type
The type of the dense elements.
llvm::hash_code hashCode
The computed hash code for the storage data.
An attribute representing a reference to a dense vector or tensor object.
DenseTypedElementsAttrStorage(ShapedType ty, ArrayRef< char > data)
bool operator==(const KeyTy &key) const
Compare this storage instance with the provided key.
static DenseTypedElementsAttrStorage * construct(AttributeStorageAllocator &allocator, KeyTy key)
Construct a new storage instance.
static llvm::hash_code hashKey(const KeyTy &key)
Hash the key for the storage.
static KeyTy getKey(ShapedType ty, ArrayRef< char > data)
Construct a key from a shaped type and raw data buffer.
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.