MLIR  16.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  : 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)
57  : DenseElementsAttributeStorage(ty, isSplat), data(data) {}
58 
59  struct KeyTy {
60  KeyTy(ShapedType type, ArrayRef<char> data, llvm::hash_code hashCode,
61  bool isSplat = false)
62  : type(type), data(data), hashCode(hashCode), isSplat(isSplat) {}
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  if (key.type != type)
80  return false;
81 
82  // For boolean splats we need to explicitly check that the first bit is the
83  // same. Boolean values are packed at the bit level, and even though a splat
84  // is detected the rest of the bits in the first byte may differ from the
85  // splat value.
86  if (key.type.getElementType().isInteger(1)) {
87  if (key.isSplat != isSplat)
88  return false;
89  if (isSplat)
90  return (key.data.front() & 1) == data.front();
91  }
92 
93  // Otherwise, we can default to just checking the data.
94  return key.data == data;
95  }
96 
97  /// Construct a key from a shaped type, raw data buffer, and a flag that
98  /// signals if the data is already known to be a splat. Callers to this
99  /// function are expected to tag preknown splat values when possible, e.g. one
100  /// element shapes.
101  static KeyTy getKey(ShapedType ty, ArrayRef<char> data, bool isKnownSplat) {
102  // Handle an empty storage instance.
103  if (data.empty())
104  return KeyTy(ty, data, 0);
105 
106  // If the data is already known to be a splat, the key hash value is
107  // directly the data buffer.
108  if (isKnownSplat)
109  return KeyTy(ty, data, llvm::hash_value(data), isKnownSplat);
110 
111  // Otherwise, we need to check if the data corresponds to a splat or not.
112 
113  // Handle the simple case of only one element.
114  size_t numElements = ty.getNumElements();
115  assert(numElements != 1 && "splat of 1 element should already be detected");
116 
117  // Handle boolean values directly as they are packed to 1-bit.
118  if (ty.getElementType().isInteger(1) == 1)
119  return getKeyForBoolData(ty, data, numElements);
120 
121  size_t elementWidth = getDenseElementBitWidth(ty.getElementType());
122  // Non 1-bit dense elements are padded to 8-bits.
123  size_t storageSize = llvm::divideCeil(elementWidth, CHAR_BIT);
124  assert(((data.size() / storageSize) == numElements) &&
125  "data does not hold expected number of elements");
126 
127  // Create the initial hash value with just the first element.
128  auto firstElt = data.take_front(storageSize);
129  auto hashVal = llvm::hash_value(firstElt);
130 
131  // Check to see if this storage represents a splat. If it doesn't then
132  // combine the hash for the data starting with the first non splat element.
133  for (size_t i = storageSize, e = data.size(); i != e; i += storageSize)
134  if (memcmp(data.data(), &data[i], storageSize))
135  return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
136 
137  // Otherwise, this is a splat so just return the hash of the first element.
138  return KeyTy(ty, firstElt, hashVal, /*isSplat=*/true);
139  }
140 
141  /// Construct a key with a set of boolean data.
142  static KeyTy getKeyForBoolData(ShapedType ty, ArrayRef<char> data,
143  size_t numElements) {
144  ArrayRef<char> splatData = data;
145  bool splatValue = splatData.front() & 1;
146 
147  // Helper functor to generate a KeyTy for a boolean splat value.
148  auto generateSplatKey = [=] {
149  return KeyTy(ty, data.take_front(1),
150  llvm::hash_value(ArrayRef<char>(splatValue ? 1 : 0)),
151  /*isSplat=*/true);
152  };
153 
154  // Handle the case where the potential splat value is 1 and the number of
155  // elements is non 8-bit aligned.
156  size_t numOddElements = numElements % CHAR_BIT;
157  if (splatValue && numOddElements != 0) {
158  // Check that all bits are set in the last value.
159  char lastElt = splatData.back();
160  if (lastElt != llvm::maskTrailingOnes<unsigned char>(numOddElements))
161  return KeyTy(ty, data, llvm::hash_value(data));
162 
163  // If this is the only element, the data is known to be a splat.
164  if (splatData.size() == 1)
165  return generateSplatKey();
166  splatData = splatData.drop_back();
167  }
168 
169  // Check that the data buffer corresponds to a splat of the proper mask.
170  char mask = splatValue ? ~0 : 0;
171  return llvm::all_of(splatData, [mask](char c) { return c == mask; })
172  ? generateSplatKey()
173  : KeyTy(ty, data, llvm::hash_value(data));
174  }
175 
176  /// Hash the key for the storage.
177  static llvm::hash_code hashKey(const KeyTy &key) {
178  return llvm::hash_combine(key.type, key.hashCode);
179  }
180 
181  /// Construct a new storage instance.
184  // If the data buffer is non-empty, we copy it into the allocator with a
185  // 64-bit alignment.
186  ArrayRef<char> copy, data = key.data;
187  if (!data.empty()) {
188  char *rawData = reinterpret_cast<char *>(
189  allocator.allocate(data.size(), alignof(uint64_t)));
190  std::memcpy(rawData, data.data(), data.size());
191 
192  // If this is a boolean splat, make sure only the first bit is used.
193  if (key.isSplat && key.type.getElementType().isInteger(1))
194  rawData[0] &= 1;
195  copy = ArrayRef<char>(rawData, data.size());
196  }
197 
198  return new (allocator.allocate<DenseIntOrFPElementsAttrStorage>())
200  }
201 
203 };
204 
205 /// An attribute representing a reference to a dense vector or tensor object
206 /// containing strings.
209  bool isSplat = false)
210  : DenseElementsAttributeStorage(ty, isSplat), data(data) {}
211 
212  struct KeyTy {
213  KeyTy(ShapedType type, ArrayRef<StringRef> data, llvm::hash_code hashCode,
214  bool isSplat = false)
215  : type(type), data(data), hashCode(hashCode), isSplat(isSplat) {}
216 
217  /// The type of the dense elements.
218  ShapedType type;
219 
220  /// The raw buffer for the data storage.
222 
223  /// The computed hash code for the storage data.
224  llvm::hash_code hashCode;
225 
226  /// A boolean that indicates if this data is a splat or not.
227  bool isSplat;
228  };
229 
230  /// Compare this storage instance with the provided key.
231  bool operator==(const KeyTy &key) const {
232  if (key.type != type)
233  return false;
234 
235  // Otherwise, we can default to just checking the data. StringRefs compare
236  // by contents.
237  return key.data == data;
238  }
239 
240  /// Construct a key from a shaped type, StringRef data buffer, and a flag that
241  /// signals if the data is already known to be a splat. Callers to this
242  /// function are expected to tag preknown splat values when possible, e.g. one
243  /// element shapes.
244  static KeyTy getKey(ShapedType ty, ArrayRef<StringRef> data,
245  bool isKnownSplat) {
246  // Handle an empty storage instance.
247  if (data.empty())
248  return KeyTy(ty, data, 0);
249 
250  // If the data is already known to be a splat, the key hash value is
251  // directly the data buffer.
252  if (isKnownSplat)
253  return KeyTy(ty, data, llvm::hash_value(data.front()), isKnownSplat);
254 
255  // Handle the simple case of only one element.
256  assert(ty.getNumElements() != 1 &&
257  "splat of 1 element should already be detected");
258 
259  // Create the initial hash value with just the first element.
260  const auto &firstElt = data.front();
261  auto hashVal = llvm::hash_value(firstElt);
262 
263  // Check to see if this storage represents a splat. If it doesn't then
264  // combine the hash for the data starting with the first non splat element.
265  for (size_t i = 1, e = data.size(); i != e; i++)
266  if (!firstElt.equals(data[i]))
267  return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
268 
269  // Otherwise, this is a splat so just return the hash of the first element.
270  return KeyTy(ty, data.take_front(), hashVal, /*isSplat=*/true);
271  }
272 
273  /// Hash the key for the storage.
274  static llvm::hash_code hashKey(const KeyTy &key) {
275  return llvm::hash_combine(key.type, key.hashCode);
276  }
277 
278  /// Construct a new storage instance.
281  // If the data buffer is non-empty, we copy it into the allocator with a
282  // 64-bit alignment.
283  ArrayRef<StringRef> copy, data = key.data;
284  if (data.empty()) {
285  return new (allocator.allocate<DenseStringElementsAttrStorage>())
287  }
288 
289  int numEntries = key.isSplat ? 1 : data.size();
290 
291  // Compute the amount data needed to store the ArrayRef and StringRef
292  // contents.
293  size_t dataSize = sizeof(StringRef) * numEntries;
294  for (int i = 0; i < numEntries; i++)
295  dataSize += data[i].size();
296 
297  char *rawData = reinterpret_cast<char *>(
298  allocator.allocate(dataSize, alignof(uint64_t)));
299 
300  // Setup a mutable array ref of our string refs so that we can update their
301  // contents.
302  auto mutableCopy = MutableArrayRef<StringRef>(
303  reinterpret_cast<StringRef *>(rawData), numEntries);
304  auto *stringData = rawData + numEntries * sizeof(StringRef);
305 
306  for (int i = 0; i < numEntries; i++) {
307  memcpy(stringData, data[i].data(), data[i].size());
308  mutableCopy[i] = StringRef(stringData, data[i].size());
309  stringData += data[i].size();
310  }
311 
312  copy =
313  ArrayRef<StringRef>(reinterpret_cast<StringRef *>(rawData), numEntries);
314 
315  return new (allocator.allocate<DenseStringElementsAttrStorage>())
317  }
318 
320 };
321 
322 //===----------------------------------------------------------------------===//
323 // StringAttr
324 //===----------------------------------------------------------------------===//
325 
328  : type(type), value(value), referencedDialect(nullptr) {}
329 
330  /// The hash key is a tuple of the parameter types.
331  using KeyTy = std::pair<StringRef, Type>;
332  bool operator==(const KeyTy &key) const {
333  return value == key.first && type == key.second;
334  }
335  static ::llvm::hash_code hashKey(const KeyTy &key) {
337  }
338 
339  /// Define a construction method for creating a new instance of this
340  /// storage.
342  const KeyTy &key) {
343  return new (allocator.allocate<StringAttrStorage>())
344  StringAttrStorage(allocator.copyInto(key.first), key.second);
345  }
346 
347  /// Initialize the storage given an MLIRContext.
348  void initialize(MLIRContext *context);
349 
350  /// The type of the string.
352  /// The raw string value.
353  StringRef value;
354  /// If the string value contains a dialect namespace prefix (e.g.
355  /// dialect.blah), this is the dialect referenced.
357 };
358 
359 } // namespace detail
360 } // namespace mlir
361 
362 #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.
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.
llvm::hash_code hash_value(const MPInt &x)
Redeclarations of friend declaration above to make it discoverable by lookups.
Definition: MPInt.cpp:15
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:270
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:41
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.
DenseElementsAttributeStorage(ShapedType type, bool isSplat)
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.
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)
Type type
The type of the string.
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)