Skip to content

Commit

Permalink
Fix the no-boost build
Browse files Browse the repository at this point in the history
  • Loading branch information
MikePopoloski committed May 6, 2023
1 parent 6aaf49e commit 4a087cc
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 44 deletions.
1 change: 1 addition & 0 deletions include/slang/diagnostics/Diagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <any>
#include <string>
#include <typeindex>
#include <variant>
#include <vector>

Expand Down
124 changes: 80 additions & 44 deletions include/slang/util/Hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
# include <boost/unordered/unordered_flat_set.hpp>
# include <boost/unordered/unordered_node_map.hpp>
# include <boost/unordered/unordered_node_set.hpp>
#else
# include <memory>
# include <unordered_map>
# include <unordered_set>
#endif

#include "slang/util/Util.h"
Expand Down Expand Up @@ -262,70 +266,103 @@ struct HashValueImpl<Tuple, 0> {
static void apply(size_t& seed, const Tuple& tuple) { hash_combine(seed, std::get<0>(tuple)); }
};

template<typename T, size_t N>
struct StackAllocStorage {
alignas(alignof(T)) char buffer[sizeof(T) * N];
T* ptr = reinterpret_cast<T*>(buffer);
template<size_t N, size_t Align = alignof(std::max_align_t)>
class StackAllocStorage {
public:
StackAllocStorage() noexcept : ptr(buffer) {}
StackAllocStorage(const StackAllocStorage& other) = delete;
StackAllocStorage& operator=(const StackAllocStorage& other) = delete;

template<size_t ReqAlign>
char* allocate(size_t n) {
static_assert(ReqAlign <= Align, "alignment is too small for this arena");

SLANG_ASSERT(isInBuffer(ptr));
auto aligned_n = alignUp(n);
if (static_cast<decltype(aligned_n)>(buffer + N - ptr) >= aligned_n) {
char* r = ptr;
ptr += aligned_n;
return r;
}

static_assert(Align <= alignof(std::max_align_t),
"you've chosen an alignment that is larger than alignof(std::max_align_t), "
"and cannot be guaranteed by normal operator new");
return static_cast<char*>(::operator new(n));
}

void deallocate(char* p, size_t n) noexcept {
SLANG_ASSERT(isInBuffer(ptr));
if (isInBuffer(p)) {
n = alignUp(n);
if (p + n == ptr)
ptr = p;
}
else {
::operator delete(p);
}
}

private:
static constexpr size_t alignUp(size_t n) noexcept { return (n + (Align - 1)) & ~(Align - 1); }

bool isInBuffer(char* p) noexcept {
return uintptr_t(buffer) <= uintptr_t(p) && uintptr_t(p) <= uintptr_t(buffer) + N;
}

T* getBuffer() { return reinterpret_cast<T*>(buffer); }
alignas(Align) char buffer[N];
char* ptr;
};

template<typename T, size_t N>
template<typename T, size_t N, size_t Align = alignof(std::max_align_t)>
class StackAllocator {
public:
using value_type = T;
using Storage = StackAllocStorage<N, Align>;

StackAllocator(Storage& storage) noexcept : storage(storage) {
static_assert(N % Align == 0, "size N needs to be a multiple of alignment Align");
}

template<typename U>
StackAllocator(const StackAllocator<U, N, Align>& other) noexcept : storage(other.storage) {}

StackAllocator(const StackAllocator&) = default;
StackAllocator& operator=(const StackAllocator&) = delete;

StackAllocator(StackAllocStorage<T, N>* storage) noexcept : storage(storage) {}

template<typename U>
struct rebind {
using other = StackAllocator<U, N>;
using other = StackAllocator<U, N, Align>;
};

T* allocate(size_t n) {
if (N - size_t(storage->ptr - storage->getBuffer()) >= n) {
T* result = storage->ptr;
storage->ptr += n;
return result;
}

return static_cast<T*>(::operator new(sizeof(T) * n));
return reinterpret_cast<T*>(storage.template allocate<alignof(T)>(n * sizeof(T)));
}

void deallocate(T* p, std::size_t n) noexcept {
// If the pointer is in our buffer, possibly "deallocate" by moving the high water mark
// back. Otherwise it was heap allocated and we must free with delete.
if (std::less_equal<T*>()(storage->getBuffer(), p) &&
std::less<T*>()(p, storage->getBuffer() + N)) {
if (p + n == storage->ptr)
storage->ptr = p;
}
else {
::operator delete(p);
}
void deallocate(T* p, size_t n) noexcept {
storage.deallocate(reinterpret_cast<char*>(p), n * sizeof(T));
}

template<typename T1, size_t N1, class T2, size_t N2>
friend bool operator==(const StackAllocator<T1, N1>& x,
const StackAllocator<T2, N2>& y) noexcept;
template<typename T1, size_t N1, size_t A1, typename T2, size_t N2, size_t A2>
friend bool operator==(const StackAllocator<T1, N1, A1>& x,
const StackAllocator<T2, N2, A2>& y) noexcept;

private:
template<typename U, size_t M>
template<typename U, size_t M, size_t A>
friend class StackAllocator;

StackAllocStorage<T, N>* storage;
Storage& storage;
};

template<typename T, size_t N, class U, size_t M>
inline bool operator==(const StackAllocator<T, N>& x, const StackAllocator<U, M>& y) noexcept {
return N == M && x.storage == y.storage;
template<typename T1, size_t N1, size_t A1, typename T2, size_t N2, size_t A2>
inline bool operator==(const StackAllocator<T1, N1, A1>& x,
const StackAllocator<T2, N2, A2>& y) noexcept {
return N1 == N2 && A1 == A2 && &x.storage == &y.storage;
}

template<typename T, size_t N, class U, size_t M>
inline bool operator!=(const StackAllocator<T, N>& x, const StackAllocator<U, M>& y) noexcept {
template<typename T1, size_t N1, size_t A1, typename T2, size_t N2, size_t A2>
inline bool operator!=(const StackAllocator<T1, N1, A1>& x,
const StackAllocator<T2, N2, A2>& y) noexcept {
return !(x == y);
}

Expand Down Expand Up @@ -382,25 +419,24 @@ using flat_node_set = std::unordered_set<T, H, E, A>;
/// Prefer this over a normal hash map for temporary stack variables and small maps
/// where heap allocations can be avoided.
template<typename K, typename V, size_t N, typename Entry = std::pair<const K, V>,
typename Alloc = detail::hashing::StackAllocator<Entry, N>>
class SmallMap : private detail::hashing::StackAllocStorage<Entry, N>,
typename Alloc = detail::hashing::StackAllocator<Entry, N * sizeof(Entry)>>
class SmallMap : private Alloc::Storage,
public flat_hash_map<K, V, hash<K>, std::equal_to<K>, Alloc> {
using BaseType = flat_hash_map<K, V, hash<K>, std::equal_to<K>, Alloc>;

public:
SmallMap() : BaseType(Alloc(this)) {}
SmallMap() : BaseType(Alloc(*this)) {}
};

/// A hash set container that allocates room for its first `N` elements on the stack.
/// Prefer this over a normal hash set for temporary stack variables and small sets
/// where heap allocations can be avoided.
template<typename T, size_t N, typename Alloc = detail::hashing::StackAllocator<T, N>>
class SmallSet : private detail::hashing::StackAllocStorage<T, N>,
public flat_hash_set<T, hash<T>, std::equal_to<T>, Alloc> {
template<typename T, size_t N, typename Alloc = detail::hashing::StackAllocator<T, N * sizeof(T)>>
class SmallSet : private Alloc::Storage, public flat_hash_set<T, hash<T>, std::equal_to<T>, Alloc> {
using BaseType = flat_hash_set<T, hash<T>, std::equal_to<T>, Alloc>;

public:
SmallSet() : BaseType(Alloc(this)) {}
SmallSet() : BaseType(Alloc(*this)) {}
};

} // namespace slang

0 comments on commit 4a087cc

Please sign in to comment.