#pragma once #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" namespace clice { class StringSet { public: using ID = std::uint32_t; StringSet(llvm::BumpPtrAllocator& allocator) : allocator(allocator) { strings.emplace_back(); } StringSet(const StringSet&) = delete; StringSet(StringSet&&) = delete; StringSet& operator=(const StringSet&) = delete; StringSet& operator=(StringSet&&) = delete; ~StringSet() = default; ID get(llvm::StringRef s) { if(s.empty()) { return ID(0); } auto [it, success] = cache.try_emplace(s, ID(0)); if(!success) { return it->second; } const auto size = s.size(); auto p = allocator.Allocate(size + 1); std::memcpy(p, s.data(), size); p[size] = '\0'; it->first = llvm::StringRef(p, size); it->second = ID(strings.size()); strings.emplace_back(it->first); return it->second; } llvm::StringRef get(ID id) { assert(id < strings.size()); return strings[id]; } llvm::StringRef save(llvm::StringRef s) { return get(get(s)); } private: llvm::BumpPtrAllocator& allocator; std::vector strings; llvm::DenseMap cache; }; template struct object_ptr { T* ptr = nullptr; object_ptr() noexcept = default; object_ptr(std::nullptr_t) noexcept : ptr(nullptr) {} explicit object_ptr(T* p) noexcept : ptr(p) {} T& operator*() const noexcept { return *ptr; } T* operator->() const noexcept { return ptr; } explicit operator bool() const noexcept { return ptr != nullptr; } std::strong_ordering operator<=>(const object_ptr&) const = default; }; template object_ptr(T*) -> object_ptr; template class ObjectSet { public: using ID = std::uint32_t; ObjectSet(llvm::BumpPtrAllocator& allocator) : allocator(allocator) {} ObjectSet(const ObjectSet&) = delete; ObjectSet(ObjectSet&&) = delete; ObjectSet& operator=(const ObjectSet&) = delete; ObjectSet& operator=(ObjectSet&&) = delete; ~ObjectSet() { if constexpr(!std::is_trivially_destructible_v) { for(auto object: objects) { if(object) { std::destroy_at(object.ptr); } } for(auto& [object, _]: removed) { if(object) { std::destroy_at(object.ptr); } } } } ID get(const T& object) { auto [it, success] = cache.try_emplace(object_ptr(const_cast(&object)), ID(0)); if(!success) { return it->second; } if(!removed.empty()) [[unlikely]] { auto [o, id] = removed.pop_back_val(); /// Reuse the old memory std::destroy_at(o.ptr); new (o.ptr) T(object); it->first = o; it->second = id; /// Resume the object id. objects[id] = o; } else { /// Alloc the new memory. auto p = allocator.Allocate(1); p = new (p) T(object); it->first = object_ptr{p}; it->second = ID(objects.size()); objects.emplace_back(p); } return it->second; } object_ptr get(ID id) { assert(id < objects.size()); return objects[id]; } object_ptr save(const T& object) { return this->get(this->get(object)); } void remove(object_ptr object) { auto it = cache.find(object_ptr(object.ptr)); if(it == cache.end()) { return; } auto id = it->second; removed.emplace_back(object, id); cache.erase(it); objects[id] = nullptr; } private: llvm::BumpPtrAllocator& allocator; std::vector> objects; llvm::SmallVector, ID>> removed; llvm::DenseMap, ID> cache; }; } // namespace clice namespace llvm { template struct DenseMapInfo> { using U = std::remove_cvref_t; using O = clice::object_ptr; inline static O getEmptyKey() { return O(DenseMapInfo::getEmptyKey()); } inline static O getTombstoneKey() { return O(DenseMapInfo::getTombstoneKey()); } inline static unsigned getHashValue(O value) { return DenseMapInfo::getHashValue(*value); } inline static bool isEqual(O lhs, O rhs) { if(lhs == rhs) { return true; }; const O Empty = getEmptyKey(); const O Tombstone = getTombstoneKey(); if(lhs == Empty || rhs == Empty || lhs == Tombstone || rhs == Tombstone) { return false; } return DenseMapInfo::isEqual(*lhs, *rhs); } }; } // namespace llvm