Files
TheRock/compiler/amd-llvm/mlir/lib/IR/AttributeDetail.h
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

341 lines
12 KiB
C++

//===- AttributeDetail.h - MLIR Affine Map details Class --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This holds implementation details of Attribute.
//
//===----------------------------------------------------------------------===//
#ifndef ATTRIBUTEDETAIL_H_
#define ATTRIBUTEDETAIL_H_
#include "mlir/IR/AffineMap.h"
#include "mlir/IR/AttributeSupport.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/BuiltinTypeInterfaces.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/IntegerSet.h"
#include "mlir/IR/MLIRContext.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/Support/Allocator.h"
#include <mutex>
namespace mlir {
namespace detail {
//===----------------------------------------------------------------------===//
// Elements Attributes
//===----------------------------------------------------------------------===//
/// Return the bit width which DenseElementsAttr should use for this type.
inline size_t getDenseElementBitWidth(Type eltType) {
if (auto denseEltType = llvm::dyn_cast<DenseElementType>(eltType))
return denseEltType.getDenseElementBitSize();
llvm_unreachable("unsupported element type");
}
/// An attribute representing a reference to a dense vector or tensor object.
struct DenseElementsAttributeStorage : public AttributeStorage {
public:
DenseElementsAttributeStorage(ShapedType type) : type(type) {}
ShapedType type;
};
/// An attribute representing a reference to a dense vector or tensor object.
struct DenseTypedElementsAttrStorage : public DenseElementsAttributeStorage {
DenseTypedElementsAttrStorage(ShapedType ty, ArrayRef<char> data)
: DenseElementsAttributeStorage(ty), data(data) {}
struct KeyTy {
KeyTy(ShapedType type, ArrayRef<char> data, llvm::hash_code hashCode)
: type(type), data(data), hashCode(hashCode) {}
/// The type of the dense elements.
ShapedType type;
/// The raw buffer for the data storage.
ArrayRef<char> data;
/// The computed hash code for the storage data.
llvm::hash_code hashCode;
};
/// Compare this storage instance with the provided key.
bool operator==(const KeyTy &key) const {
return key.type == type && key.data == data;
}
/// Construct a key from a shaped type and raw data buffer.
static KeyTy getKey(ShapedType ty, ArrayRef<char> data) {
// Handle an empty storage instance.
if (data.empty())
return KeyTy(ty, data, 0);
size_t elementWidth = getDenseElementBitWidth(ty.getElementType());
// Dense elements are padded to 8-bits.
size_t storageSize = llvm::divideCeil(elementWidth, CHAR_BIT);
// If the data buffer holds a single element, it is a known splat.
if (data.size() == storageSize)
return KeyTy(ty, data, llvm::hash_value(data));
assert(((data.size() / storageSize) ==
static_cast<size_t>(ty.getNumElements())) &&
"data does not hold expected number of elements");
// Create the initial hash value with just the first element.
auto firstElt = data.take_front(storageSize);
auto hashVal = llvm::hash_value(firstElt);
// Check to see if this storage represents a splat. If it doesn't then
// combine the hash for the data starting with the first non splat element.
for (size_t i = storageSize, e = data.size(); i != e; i += storageSize)
if (memcmp(data.data(), &data[i], storageSize))
return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
// Otherwise, this is a splat so just return the hash of the first element.
return KeyTy(ty, firstElt, hashVal);
}
/// Hash the key for the storage.
static llvm::hash_code hashKey(const KeyTy &key) {
return llvm::hash_combine(key.type, key.hashCode);
}
/// Construct a new storage instance.
static DenseTypedElementsAttrStorage *
construct(AttributeStorageAllocator &allocator, KeyTy key) {
// If the data buffer is non-empty, we copy it into the allocator with a
// 64-bit alignment.
ArrayRef<char> copy, data = key.data;
if (!data.empty()) {
char *rawData = reinterpret_cast<char *>(
allocator.allocate(data.size(), alignof(uint64_t)));
std::memcpy(rawData, data.data(), data.size());
copy = ArrayRef<char>(rawData, data.size());
}
return new (allocator.allocate<DenseTypedElementsAttrStorage>())
DenseTypedElementsAttrStorage(key.type, copy);
}
ArrayRef<char> data;
};
/// An attribute representing a reference to a dense vector or tensor object
/// containing strings.
struct DenseStringElementsAttrStorage : public DenseElementsAttributeStorage {
DenseStringElementsAttrStorage(ShapedType ty, ArrayRef<StringRef> data)
: DenseElementsAttributeStorage(ty), data(data) {}
struct KeyTy {
KeyTy(ShapedType type, ArrayRef<StringRef> data, llvm::hash_code hashCode)
: type(type), data(data), hashCode(hashCode) {}
/// The type of the dense elements.
ShapedType type;
/// The raw buffer for the data storage.
ArrayRef<StringRef> data;
/// The computed hash code for the storage data.
llvm::hash_code hashCode;
};
/// Compare this storage instance with the provided key.
bool operator==(const KeyTy &key) const {
if (key.type != type)
return false;
// Otherwise, we can default to just checking the data. StringRefs compare
// by contents.
return key.data == data;
}
/// Construct a key from a shaped type and StringRef data buffer.
static KeyTy getKey(ShapedType ty, ArrayRef<StringRef> data) {
// Handle an empty storage instance.
if (data.empty())
return KeyTy(ty, data, 0);
// If the data buffer holds a single element, it is a known splat.
if (data.size() == 1)
return KeyTy(ty, data, llvm::hash_value(data.front()));
// Create the initial hash value with just the first element.
const auto &firstElt = data.front();
auto hashVal = llvm::hash_value(firstElt);
// Check to see if this storage represents a splat. If it doesn't then
// combine the hash for the data starting with the first non splat element.
for (size_t i = 1, e = data.size(); i != e; ++i)
if (firstElt != data[i])
return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i)));
// Otherwise, this is a splat so just return the hash of the first element.
return KeyTy(ty, data.take_front(), hashVal);
}
/// Hash the key for the storage.
static llvm::hash_code hashKey(const KeyTy &key) {
return llvm::hash_combine(key.type, key.hashCode);
}
/// Construct a new storage instance.
static DenseStringElementsAttrStorage *
construct(AttributeStorageAllocator &allocator, KeyTy key) {
// If the data buffer is non-empty, we copy it into the allocator with a
// 64-bit alignment.
ArrayRef<StringRef> copy, data = key.data;
if (data.empty()) {
return new (allocator.allocate<DenseStringElementsAttrStorage>())
DenseStringElementsAttrStorage(key.type, copy);
}
size_t numEntries = data.size();
// Compute the amount data needed to store the ArrayRef and StringRef
// contents.
size_t dataSize = sizeof(StringRef) * numEntries;
for (size_t i = 0; i < numEntries; ++i)
dataSize += data[i].size();
char *rawData = reinterpret_cast<char *>(
allocator.allocate(dataSize, alignof(uint64_t)));
// Setup a mutable array ref of our string refs so that we can update their
// contents.
auto mutableCopy = MutableArrayRef<StringRef>(
reinterpret_cast<StringRef *>(rawData), numEntries);
auto *stringData = rawData + numEntries * sizeof(StringRef);
for (size_t i = 0; i < numEntries; ++i) {
memcpy(stringData, data[i].data(), data[i].size());
mutableCopy[i] = StringRef(stringData, data[i].size());
stringData += data[i].size();
}
copy =
ArrayRef<StringRef>(reinterpret_cast<StringRef *>(rawData), numEntries);
return new (allocator.allocate<DenseStringElementsAttrStorage>())
DenseStringElementsAttrStorage(key.type, copy);
}
ArrayRef<StringRef> data;
};
//===----------------------------------------------------------------------===//
// StringAttr
//===----------------------------------------------------------------------===//
struct StringAttrStorage : public AttributeStorage {
StringAttrStorage(StringRef value, Type type)
: type(type), value(value), referencedDialect(nullptr) {}
/// The hash key is a tuple of the parameter types.
using KeyTy = std::pair<StringRef, Type>;
bool operator==(const KeyTy &key) const {
return value == key.first && type == key.second;
}
static ::llvm::hash_code hashKey(const KeyTy &key) {
return DenseMapInfo<KeyTy>::getHashValue(key);
}
/// Define a construction method for creating a new instance of this
/// storage.
static StringAttrStorage *construct(AttributeStorageAllocator &allocator,
const KeyTy &key) {
return new (allocator.allocate<StringAttrStorage>())
StringAttrStorage(allocator.copyInto(key.first), key.second);
}
/// Initialize the storage given an MLIRContext.
void initialize(MLIRContext *context);
/// The type of the string.
Type type;
/// The raw string value.
StringRef value;
/// If the string value contains a dialect namespace prefix (e.g.
/// dialect.blah), this is the dialect referenced.
Dialect *referencedDialect;
};
//===----------------------------------------------------------------------===//
// DistinctAttr
//===----------------------------------------------------------------------===//
/// An attribute to store a distinct reference to another attribute.
struct DistinctAttrStorage : public AttributeStorage {
using KeyTy = Attribute;
DistinctAttrStorage(Attribute referencedAttr)
: referencedAttr(referencedAttr) {}
/// Returns the referenced attribute as key.
KeyTy getAsKey() const { return KeyTy(referencedAttr); }
/// The referenced attribute.
Attribute referencedAttr;
};
/// A specialized attribute uniquer for distinct attributes that always
/// allocates since the distinct attribute instances use the address of their
/// storage as unique identifier.
class DistinctAttributeUniquer {
public:
/// Creates a distinct attribute storage. Allocates every time since the
/// address of the storage serves as unique identifier.
template <typename T, typename... Args>
static T get(MLIRContext *context, Args &&...args) {
static_assert(std::is_same_v<typename T::ImplType, DistinctAttrStorage>,
"expects a distinct attribute storage");
DistinctAttrStorage *storage = DistinctAttributeUniquer::allocateStorage(
context, std::forward<Args>(args)...);
storage->initializeAbstractAttribute(
AbstractAttribute::lookup(DistinctAttr::getTypeID(), context));
return storage;
}
private:
/// Allocates a distinct attribute storage.
static DistinctAttrStorage *allocateStorage(MLIRContext *context,
Attribute referencedAttr);
};
/// An allocator for distinct attribute storage instances. Uses a synchronized
/// BumpPtrAllocator to ensure thread-safety. The allocated storage is deleted
/// when the DistinctAttributeAllocator is destroyed.
class DistinctAttributeAllocator final {
public:
DistinctAttributeAllocator() = default;
DistinctAttributeAllocator(DistinctAttributeAllocator &&) = delete;
DistinctAttributeAllocator(const DistinctAttributeAllocator &) = delete;
DistinctAttributeAllocator &
operator=(const DistinctAttributeAllocator &) = delete;
DistinctAttrStorage *allocate(Attribute referencedAttr) {
std::scoped_lock<std::mutex> guard(allocatorMutex);
return new (allocator.Allocate<DistinctAttrStorage>())
DistinctAttrStorage(referencedAttr);
};
private:
/// Used to allocate distict attribute storages. The managed memory is freed
/// automatically when the allocator instance is destroyed.
llvm::BumpPtrAllocator allocator;
/// Used to lock access to the allocator.
std::mutex allocatorMutex;
};
} // namespace detail
} // namespace mlir
#endif // ATTRIBUTEDETAIL_H_