MLIR  14.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"
18 #include "mlir/IR/BuiltinTypes.h"
19 #include "mlir/IR/IntegerSet.h"
20 #include "mlir/IR/MLIRContext.h"
22 #include "llvm/ADT/APFloat.h"
23 #include "llvm/ADT/PointerIntPair.h"
24 #include "llvm/Support/TrailingObjects.h"
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 = eltType.dyn_cast<ComplexType>())
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  : AttributeStorage(ty), isSplat(isSplat) {}
48 
49  bool isSplat;
50 };
51 
52 /// An attribute representing a reference to a dense vector or tensor object.
55  bool isSplat = false)
56  : DenseElementsAttributeStorage(ty, isSplat), data(data) {}
57 
58  struct KeyTy {
59  KeyTy(ShapedType type, ArrayRef<char> data, llvm::hash_code hashCode,
60  bool isSplat = false)
61  : type(type), data(data), hashCode(hashCode), isSplat(isSplat) {}
62 
63  /// The type of the dense elements.
64  ShapedType type;
65 
66  /// The raw buffer for the data storage.
68 
69  /// The computed hash code for the storage data.
70  llvm::hash_code hashCode;
71 
72  /// A boolean that indicates if this data is a splat or not.
73  bool isSplat;
74  };
75 
76  /// Compare this storage instance with the provided key.
77  bool operator==(const KeyTy &key) const {
78  if (key.type != getType())
79  return false;
80 
81  // For boolean splats we need to explicitly check that the first bit is the
82  // same. Boolean values are packed at the bit level, and even though a splat
83  // is detected the rest of the bits in the first byte may differ from the
84  // splat value.
85  if (key.type.getElementType().isInteger(1)) {
86  if (key.isSplat != isSplat)
87  return false;
88  if (isSplat)
89  return (key.data.front() & 1) == data.front();
90  }
91 
92  // Otherwise, we can default to just checking the data.
93  return key.data == data;
94  }
95 
96  /// Construct a key from a shaped type, raw data buffer, and a flag that
97  /// signals if the data is already known to be a splat. Callers to this
98  /// function are expected to tag preknown splat values when possible, e.g. one
99  /// element shapes.
100  static KeyTy getKey(ShapedType ty, ArrayRef<char> data, bool isKnownSplat) {
101  // Handle an empty storage instance.
102  if (data.empty())
103  return KeyTy(ty, data, 0);
104 
105  // If the data is already known to be a splat, the key hash value is
106  // directly the data buffer.
107  if (isKnownSplat)
108  return KeyTy(ty, data, llvm::hash_value(data), isKnownSplat);
109 
110  // Otherwise, we need to check if the data corresponds to a splat or not.
111 
112  // Handle the simple case of only one element.
113  size_t numElements = ty.getNumElements();
114  assert(numElements != 1 && "splat of 1 element should already be detected");
115 
116  // Handle boolean values directly as they are packed to 1-bit.
117  if (ty.getElementType().isInteger(1) == 1)
118  return getKeyForBoolData(ty, data, numElements);
119 
120  size_t elementWidth = getDenseElementBitWidth(ty.getElementType());
121  // Non 1-bit dense elements are padded to 8-bits.
122  size_t storageSize = llvm::divideCeil(elementWidth, CHAR_BIT);
123  assert(((data.size() / storageSize) == numElements) &&
124  "data does not hold expected number of elements");
125 
126  // Create the initial hash value with just the first element.
127  auto firstElt = data.take_front(storageSize);
128  auto hashVal = llvm::hash_value(firstElt);
129 
130  // Check to see if this storage represents a splat. If it doesn't then
131  // combine the hash for the data starting with the first non splat element.
132  for (size_t i = storageSize, e = data.size(); i != e; i += storageSize)
133  if (memcmp(data.data(), &data[i], storageSize))
134  return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
135 
136  // Otherwise, this is a splat so just return the hash of the first element.
137  return KeyTy(ty, firstElt, hashVal, /*isSplat=*/true);
138  }
139 
140  /// Construct a key with a set of boolean data.
141  static KeyTy getKeyForBoolData(ShapedType ty, ArrayRef<char> data,
142  size_t numElements) {
143  ArrayRef<char> splatData = data;
144  bool splatValue = splatData.front() & 1;
145 
146  // Helper functor to generate a KeyTy for a boolean splat value.
147  auto generateSplatKey = [=] {
148  return KeyTy(ty, data.take_front(1),
149  llvm::hash_value(ArrayRef<char>(splatValue ? 1 : 0)),
150  /*isSplat=*/true);
151  };
152 
153  // Handle the case where the potential splat value is 1 and the number of
154  // elements is non 8-bit aligned.
155  size_t numOddElements = numElements % CHAR_BIT;
156  if (splatValue && numOddElements != 0) {
157  // Check that all bits are set in the last value.
158  char lastElt = splatData.back();
159  if (lastElt != llvm::maskTrailingOnes<unsigned char>(numOddElements))
160  return KeyTy(ty, data, llvm::hash_value(data));
161 
162  // If this is the only element, the data is known to be a splat.
163  if (splatData.size() == 1)
164  return generateSplatKey();
165  splatData = splatData.drop_back();
166  }
167 
168  // Check that the data buffer corresponds to a splat of the proper mask.
169  char mask = splatValue ? ~0 : 0;
170  return llvm::all_of(splatData, [mask](char c) { return c == mask; })
171  ? generateSplatKey()
172  : KeyTy(ty, data, llvm::hash_value(data));
173  }
174 
175  /// Hash the key for the storage.
176  static llvm::hash_code hashKey(const KeyTy &key) {
177  return llvm::hash_combine(key.type, key.hashCode);
178  }
179 
180  /// Construct a new storage instance.
183  // If the data buffer is non-empty, we copy it into the allocator with a
184  // 64-bit alignment.
185  ArrayRef<char> copy, data = key.data;
186  if (!data.empty()) {
187  char *rawData = reinterpret_cast<char *>(
188  allocator.allocate(data.size(), alignof(uint64_t)));
189  std::memcpy(rawData, data.data(), data.size());
190 
191  // If this is a boolean splat, make sure only the first bit is used.
192  if (key.isSplat && key.type.getElementType().isInteger(1))
193  rawData[0] &= 1;
194  copy = ArrayRef<char>(rawData, data.size());
195  }
196 
197  return new (allocator.allocate<DenseIntOrFPElementsAttrStorage>())
199  }
200 
202 };
203 
204 /// An attribute representing a reference to a dense vector or tensor object
205 /// containing strings.
208  bool isSplat = false)
209  : DenseElementsAttributeStorage(ty, isSplat), data(data) {}
210 
211  struct KeyTy {
212  KeyTy(ShapedType type, ArrayRef<StringRef> data, llvm::hash_code hashCode,
213  bool isSplat = false)
214  : type(type), data(data), hashCode(hashCode), isSplat(isSplat) {}
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 != getType())
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.equals(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.
282  ArrayRef<StringRef> copy, data = key.data;
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 
326  StringAttrStorage(StringRef value, Type type)
327  : AttributeStorage(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 && getType() == 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 raw string value.
350  StringRef value;
351  /// If the string value contains a dialect namespace prefix (e.g.
352  /// dialect.blah), this is the dialect referenced.
354 };
355 
356 } // namespace detail
357 } // namespace mlir
358 
359 #endif // ATTRIBUTEDETAIL_H_
Include the generated interface declarations.
ArrayRef< char > data
The raw buffer for the data storage.
Dialect * referencedDialect
If the string value contains a dialect namespace prefix (e.g.
StringRef value
The raw string value.
KeyTy(ShapedType type, ArrayRef< StringRef > data, llvm::hash_code hashCode, bool isSplat=false)
static llvm::hash_code hashKey(const KeyTy &key)
Hash the key for the storage.
Type getType() const
Get the type of this attribute.
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
This is a utility allocator used to allocate memory for instances of derived types.
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...
bool isSplat
A boolean that indicates if this data is a splat or not.
bool isSplat
A boolean that indicates if this data is a splat or not.
static KeyTy getKeyForBoolData(ShapedType ty, ArrayRef< char > data, size_t numElements)
Construct a key with a set of boolean data.
static constexpr const bool value
static DenseIntOrFPElementsAttrStorage * construct(AttributeStorageAllocator &allocator, KeyTy key)
Construct a new storage instance.
bool operator==(const KeyTy &key) const
T * allocate()
Allocate an instance of the provided type.
::llvm::hash_code hashKey(const KeyTy &key)
An attribute representing a reference to a dense vector or tensor object containing strings...
U dyn_cast() const
Definition: Types.h:244
An attribute representing a reference to a dense vector or tensor object.
bool isIndex() const
Definition: Types.cpp:28
An attribute representing a reference to a dense vector or tensor object.
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition: Dialect.h:42
std::pair< StringRef, Type > KeyTy
The hash key is a tuple of the parameter types.
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.
void initialize(MLIRContext *context)
Default initialization for attribute storage classes that require no additional initialization.
ShapedType type
The type of the dense elements.
llvm::hash_code hashCode
The computed hash code for the storage data.
size_t getDenseElementBitWidth(Type eltType)
Return the bit width which DenseElementsAttr should use for this type.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:72
static DenseStringElementsAttrStorage * construct(AttributeStorageAllocator &allocator, KeyTy key)
Construct a new storage instance.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:55
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition: Types.cpp:91
bool operator==(const KeyTy &key) const
Compare this storage instance with the provided key.
StringAttrStorage(StringRef value, Type type)
Base storage class appearing in an attribute.
DenseElementsAttributeStorage(ShapedType ty, bool isSplat)
ArrayRef< T > copyInto(ArrayRef< T > elements)
Copy the specified array of elements into memory managed by our bump pointer allocator.
static StringAttrStorage * construct(AttributeStorageAllocator &allocator, const KeyTy &key)
Define a construction method for creating a new instance of this storage.
llvm::hash_code hashCode
The computed hash code for the storage data.
DenseIntOrFPElementsAttrStorage(ShapedType ty, ArrayRef< char > data, bool isSplat=false)
llvm::hash_code hash_value(const StructType::MemberDecorationInfo &memberDecorationInfo)
static llvm::hash_code hashKey(const KeyTy &key)
Hash the key for the storage.
ArrayRef< StringRef > data
The raw buffer for the data storage.
KeyTy(ShapedType type, ArrayRef< char > data, llvm::hash_code hashCode, bool isSplat=false)
ShapedType type
The type of the dense elements.
DenseStringElementsAttrStorage(ShapedType ty, ArrayRef< StringRef > data, bool isSplat=false)