diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index a27e4d43f..ea0f674b1 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -43,6 +43,7 @@ yosys_core(kernel gzip.cc gzip.h hashcons.h + hashcons_impl.h hashlib.h io.cc io.h @@ -133,6 +134,7 @@ yosys_core(kernel fmt.h gzip.h hashcons.h + hashcons_impl.h hashlib.h io.h json.h diff --git a/kernel/hashcons.h b/kernel/hashcons.h index a4e02208b..5da2a4c8e 100644 --- a/kernel/hashcons.h +++ b/kernel/hashcons.h @@ -3,7 +3,6 @@ #include "kernel/yosys_common.h" -#include #include #include #include @@ -25,28 +24,13 @@ public: Derived* self() { return static_cast(this); } const Derived* self() const { return static_cast(this); } - HashConsPool() { rebuild_index(); } + HashConsPool(); HashConsPool(const HashConsPool& other) = default; - HashConsPool(HashConsPool&& other) - : backing(std::move(other.backing)), table(std::move(other.table)), free_list(std::move(other.free_list)) { - other.reset(); - } + HashConsPool(HashConsPool&& other); HashConsPool& operator=(const HashConsPool& other) = default; - HashConsPool& operator=(HashConsPool&& other) { - if (this != &other) { - backing = std::move(other.backing); - table = std::move(other.table); - free_list = std::move(other.free_list); - other.reset(); - } - return *this; - } + HashConsPool& operator=(HashConsPool&& other); - void reset() { - backing.clear(); - free_list.clear(); - rebuild_index(); - } + void reset(); static bool is_static(Ref ref) { if constexpr (Derived::STATIC_COUNT == 0) @@ -115,17 +99,7 @@ public: static void check_ready() {} - void rebuild_index() { - Derived::check_ready(); - free_list.clear(); - for (size_t idx = 0; idx < backing.size(); ++idx) - if (backing[idx].is_dead()) - free_list.push_back(idx); - std::sort(free_list.begin(), free_list.end(), std::greater()); - table.assign(std::bit_ceil((Derived::STATIC_COUNT + size()) * 2 + 2), Ref()); - for (Ref ref : refs()) - index_insert(ref); - } + void rebuild_index(); size_t home_slot(uint64_t hash) const { // Knuth's Fibonacci hashing saves us from how bad DJB2 is @@ -135,67 +109,11 @@ public: size_t next_slot(size_t slot) const { return (slot + 1) & (table.size() - 1); } template - Ref find_hashed(uint64_t hash, Eq&& eq) const { - for (size_t slot = home_slot(hash); table[slot] != Ref(); slot = next_slot(slot)) { - Ref ref = table[slot]; - if (Derived::hash_node((*this)[ref]) == hash && eq(ref)) - return ref; - } - return Ref(); - } - - void index_insert(Ref ref) { - size_t slot = home_slot(Derived::hash_node((*this)[ref])); - while (table[slot] != Ref()) - slot = next_slot(slot); - table[slot] = ref; - } - - void index_erase(Ref ref) { - size_t hole = home_slot(Derived::hash_node((*this)[ref])); - while (table[hole] != ref) - hole = next_slot(hole); - size_t mask = table.size() - 1; - for (size_t slot = next_slot(hole); table[slot] != Ref(); slot = next_slot(slot)) { - size_t home = home_slot(Derived::hash_node((*this)[table[slot]])); - if (((slot - home) & mask) >= ((slot - hole) & mask)) { - table[hole] = table[slot]; - hole = slot; - } - } - table[hole] = Ref(); - } - - void grow_index() { - std::vector old(table.size() * 2, Ref()); - std::swap(old, table); - for (Ref ref : old) - if (ref != Ref()) - index_insert(ref); - } - - Ref add_inner(Node t) { - Ref ref; - if (!free_list.empty()) { - size_t idx = free_list.back(); - free_list.pop_back(); - backing[idx] = std::move(t); - ref = Ref(Derived::STATIC_COUNT + idx); - } else { - ref = Ref(Derived::STATIC_COUNT + backing.size()); - backing.push_back(std::move(t)); - } - if ((Derived::STATIC_COUNT + size()) * 3 > table.size() * 2) - grow_index(); - index_insert(ref); - if (yosys_xtrace) { - std::cout << "#X# add_inner added "; - self()->dump(ref); - std::cout << "\n"; - std::cout << "#X# as integer " << ref.raw() << "\n"; - } - return ref; - } + Ref find_hashed(uint64_t hash, Eq&& eq) const; + void index_insert(Ref ref); + void index_erase(Ref ref); + void grow_index(); + Ref add_inner(Node t); size_t size() const { return backing.size() - free_list.size(); } @@ -206,32 +124,8 @@ public: } template - size_t gc(const Roots& roots) { - pool live; - for (Ref ref : roots) - mark_live(ref, live); - size_t erased = 0; - for (size_t idx = 0; idx < backing.size(); ++idx) { - if (backing[idx].is_dead()) - continue; - if (!live.count(Ref(Derived::STATIC_COUNT + idx))) { - index_erase(Ref(Derived::STATIC_COUNT + idx)); - free_list.push_back(idx); - backing[idx] = Node{}; - erased++; - } - } - // TODO something like YOSYS_SORT_ID_FREE_LIST to make it optional? - std::sort(free_list.begin(), free_list.end(), std::greater()); - return erased; - } - - void mark_live(Ref ref, pool& live) const { - ref = ref.untag(); - if (ref == Ref() || is_static(ref) || !live.insert(ref).second) - return; - Derived::for_each_child((*this)[ref], [&](Ref child) { mark_live(child, live); }); - } + size_t gc(const Roots& roots); + void mark_live(Ref ref, pool& live) const; }; YOSYS_NAMESPACE_END diff --git a/kernel/hashcons_impl.h b/kernel/hashcons_impl.h new file mode 100644 index 000000000..e08032596 --- /dev/null +++ b/kernel/hashcons_impl.h @@ -0,0 +1,150 @@ +#ifndef YOSYS_HASHCONS_IMPL_H +#define YOSYS_HASHCONS_IMPL_H + +#include "kernel/hashcons.h" + +#include + +YOSYS_NAMESPACE_BEGIN + +template +HashConsPool::HashConsPool() { rebuild_index(); } + +template +HashConsPool::HashConsPool(HashConsPool&& other) + : backing(std::move(other.backing)), table(std::move(other.table)), free_list(std::move(other.free_list)) { + other.reset(); +} + +template +HashConsPool& HashConsPool::operator=(HashConsPool&& other) { + if (this != &other) { + backing = std::move(other.backing); + table = std::move(other.table); + free_list = std::move(other.free_list); + other.reset(); + } + return *this; +} + +template +void HashConsPool::reset() { + backing.clear(); + free_list.clear(); + rebuild_index(); +} + +template +void HashConsPool::rebuild_index() { + Derived::check_ready(); + free_list.clear(); + for (size_t idx = 0; idx < backing.size(); ++idx) + if (backing[idx].is_dead()) + free_list.push_back(idx); + std::sort(free_list.begin(), free_list.end(), std::greater()); + table.assign(std::bit_ceil((Derived::STATIC_COUNT + size()) * 2 + 2), Ref()); + for (Ref ref : refs()) + index_insert(ref); +} + +template +template +Ref HashConsPool::find_hashed(uint64_t hash, Eq&& eq) const { + for (size_t slot = home_slot(hash); table[slot] != Ref(); slot = next_slot(slot)) { + Ref ref = table[slot]; + if (Derived::hash_node((*this)[ref]) == hash && eq(ref)) + return ref; + } + return Ref(); +} + +template +void HashConsPool::index_insert(Ref ref) { + size_t slot = home_slot(Derived::hash_node((*this)[ref])); + while (table[slot] != Ref()) + slot = next_slot(slot); + table[slot] = ref; +} + +template +void HashConsPool::index_erase(Ref ref) { + size_t hole = home_slot(Derived::hash_node((*this)[ref])); + while (table[hole] != ref) + hole = next_slot(hole); + size_t mask = table.size() - 1; + for (size_t slot = next_slot(hole); table[slot] != Ref(); slot = next_slot(slot)) { + size_t home = home_slot(Derived::hash_node((*this)[table[slot]])); + if (((slot - home) & mask) >= ((slot - hole) & mask)) { + table[hole] = table[slot]; + hole = slot; + } + } + table[hole] = Ref(); +} + +template +void HashConsPool::grow_index() { + std::vector old(table.size() * 2, Ref()); + std::swap(old, table); + for (Ref ref : old) + if (ref != Ref()) + index_insert(ref); +} + +template +Ref HashConsPool::add_inner(Node t) { + Ref ref; + if (!free_list.empty()) { + size_t idx = free_list.back(); + free_list.pop_back(); + backing[idx] = std::move(t); + ref = Ref(Derived::STATIC_COUNT + idx); + } else { + ref = Ref(Derived::STATIC_COUNT + backing.size()); + backing.push_back(std::move(t)); + } + if ((Derived::STATIC_COUNT + size()) * 3 > table.size() * 2) + grow_index(); + index_insert(ref); + if (yosys_xtrace) { + std::cout << "#X# add_inner added "; + self()->dump(ref); + std::cout << "\n"; + std::cout << "#X# as integer " << ref.raw() << "\n"; + } + return ref; +} + +template +template +size_t HashConsPool::gc(const Roots& roots) { + pool live; + for (Ref ref : roots) + mark_live(ref, live); + size_t erased = 0; + for (size_t idx = 0; idx < backing.size(); ++idx) { + if (backing[idx].is_dead()) + continue; + if (!live.count(Ref(Derived::STATIC_COUNT + idx))) { + index_erase(Ref(Derived::STATIC_COUNT + idx)); + free_list.push_back(idx); + backing[idx] = Node{}; + erased++; + } + } + // TODO something like YOSYS_SORT_ID_FREE_LIST to make it optional? + std::sort(free_list.begin(), free_list.end(), std::greater()); + return erased; +} + +template +void HashConsPool::mark_live(Ref ref, pool& live) const { + ref = ref.untag(); + if (ref == Ref() || is_static(ref) || !live.insert(ref).second) + return; + Derived::for_each_child((*this)[ref], [&](Ref child) { mark_live(child, live); }); +} + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/twine.cc b/kernel/twine.cc index d63749b3d..e88332cdf 100644 --- a/kernel/twine.cc +++ b/kernel/twine.cc @@ -1,8 +1,11 @@ #include "kernel/twine.h" +#include "kernel/hashcons_impl.h" #include "kernel/log.h" YOSYS_NAMESPACE_BEGIN +template struct HashConsPool; + std::vector StaticTwines::nodes_; void StaticTwines::init() { @@ -185,6 +188,12 @@ const TwineNode& TwinePool::operator[](IdString ref) const { return HashConsPool::operator[](ref); } +size_t TwinePool::gc(pool& roots) { + for (auto &it : auto_prefixes) + roots.insert(it.second); + return HashConsPool::gc(roots); +} + const TwineNode& TwinePool::static_node(size_t idx) { return StaticTwines::node(idx); } void TwinePool::check_ready() { log_assert(StaticTwines::ready()); } diff --git a/kernel/twine.h b/kernel/twine.h index 878a4cb74..3bcde927c 100644 --- a/kernel/twine.h +++ b/kernel/twine.h @@ -359,12 +359,7 @@ struct TwinePool : HashConsPool { f(t.prefix()); } - template - size_t gc(Roots& roots) { - for (auto &it : auto_prefixes) - roots.insert(it.second); - return HashConsPool::gc(roots); - } + size_t gc(pool& roots); void dump(IdString ref, std::ostream& os = std::cout) const;