From b0e2d75dbe26b98b2bf8a0d9702c422aa23f21f6 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Mon, 13 Oct 2025 00:12:51 +0000 Subject: [PATCH] Make IdString refcounts a hashtable containing only the nonzero refcounts This saves space and doesn't cost very much since we hardly ever have nonzero refcounts any more. It also allows for IdStrings with negative indexes, which we're going to add. --- kernel/rtlil.cc | 10 ++++++---- kernel/rtlil.h | 44 +++++++++++++++++++++++++++----------------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 0e974b141..466ed0a74 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -38,7 +38,7 @@ RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard; std::vector RTLIL::IdString::global_id_storage_; std::unordered_map RTLIL::IdString::global_id_index_; #ifndef YOSYS_NO_IDS_REFCNT -std::vector RTLIL::IdString::global_refcount_storage_; +std::unordered_map RTLIL::IdString::global_refcount_storage_; std::vector RTLIL::IdString::global_free_idx_list_; #endif @@ -61,7 +61,6 @@ void RTLIL::IdString::prepopulate() int size = static_cast(RTLIL::StaticId::STATIC_ID_END); global_id_storage_.reserve(size); global_id_index_.reserve(size); - global_refcount_storage_.resize(size, 1); RTLIL::IdString::global_id_index_.insert({"", 0}); RTLIL::IdString::global_id_storage_.push_back({const_cast(""), 0}); #define X(N) populate("\\" #N); @@ -180,17 +179,20 @@ struct IdStringCollector { void RTLIL::OwningIdString::collect_garbage() { #ifndef YOSYS_NO_IDS_REFCNT - int size = GetSize(global_refcount_storage_); + int size = GetSize(global_id_storage_); IdStringCollector collector(size); for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) { collector.trace(*design); } for (int i = static_cast(StaticId::STATIC_ID_END); i < size; ++i) { - if (collector.live[i] || global_refcount_storage_[i] > 0) + if (collector.live[i]) continue; RTLIL::IdString::Storage &storage = global_id_storage_.at(i); if (storage.buf == nullptr) continue; + if (global_refcount_storage_.find(i) != global_refcount_storage_.end()) + continue; + if (yosys_xtrace) { log("#X# Removed IdString '%s' with index %d.\n", storage.buf, i); log_backtrace("-X- ", yosys_xtrace-1); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 9d3919162..a081762c9 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -159,16 +159,18 @@ struct RTLIL::IdString static std::vector global_id_storage_; static std::unordered_map global_id_index_; #ifndef YOSYS_NO_IDS_REFCNT - // For prepopulated IdStrings, the refcount is meaningless since they - // are never freed even if the refcount is zero. For code efficiency - // we increment the refcount of prepopulated IdStrings like any other string, - // but we never decrement the refcount or check whether it's zero. - // So, make this unsigned because refcounts of preopulated IdStrings may overflow - // and overflow of signed integers is undefined behavior. - static std::vector global_refcount_storage_; + // All (index, refcount) pairs in this map have refcount > 0. + static std::unordered_map global_refcount_storage_; static std::vector global_free_idx_list_; #endif + static int refcount(int idx) { + auto it = global_refcount_storage_.find(idx); + if (it == global_refcount_storage_.end()) + return 0; + return it->second; + } + static inline void xtrace_db_dump() { #ifdef YOSYS_XTRACE_GET_PUT @@ -177,7 +179,7 @@ struct RTLIL::IdString if (global_id_storage_.at(idx).buf == nullptr) log("#X# DB-DUMP index %d: FREE\n", idx); else - log("#X# DB-DUMP index %d: '%s' (ref %u)\n", idx, global_id_storage_.at(idx).buf, global_refcount_storage_.at(idx)); + log("#X# DB-DUMP index %d: '%s' (ref %u)\n", idx, refcount(idx).buf, refcount); } #endif } @@ -197,7 +199,7 @@ struct RTLIL::IdString if (it != global_id_index_.end()) { #ifdef YOSYS_XTRACE_GET_PUT if (yosys_xtrace) - log("#X# GET-BY-NAME '%s' (index %d, refcount %u)\n", global_id_storage_.at(it->second).buf, it->second, global_refcount_storage_.at(it->second)); + log("#X# GET-BY-NAME '%s' (index %d, refcount %u)\n", global_id_storage_.at(it->second).buf, it->second, refcount(it->second)); #endif return it->second; } @@ -214,7 +216,6 @@ struct RTLIL::IdString log_assert(global_id_storage_.size() < 0x40000000); global_free_idx_list_.push_back(global_id_storage_.size()); global_id_storage_.push_back({nullptr, 0}); - global_refcount_storage_.push_back(0); } int idx = global_free_idx_list_.back(); @@ -236,7 +237,7 @@ struct RTLIL::IdString #ifdef YOSYS_XTRACE_GET_PUT if (yosys_xtrace) - log("#X# GET-BY-NAME '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx).buf, idx, global_refcount_storage_.at(idx)); + log("#X# GET-BY-NAME '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx).buf, idx, refcount(idx)); #endif return idx; @@ -443,11 +444,17 @@ private: static void get_reference(int idx) { #ifndef YOSYS_NO_IDS_REFCNT - global_refcount_storage_[idx]++; + if (idx < static_cast(StaticId::STATIC_ID_END)) + return; + auto it = global_refcount_storage_.find(idx); + if (it == global_refcount_storage_.end()) + global_refcount_storage_.insert(it, {idx, 1}); + else + ++it->second; #endif #ifdef YOSYS_XTRACE_GET_PUT if (yosys_xtrace && idx >= static_cast(StaticId::STATIC_ID_END)) - log("#X# GET-BY-INDEX '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx)); + log("#X# GET-BY-INDEX '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx), idx, refcount(idx)); #endif } @@ -459,11 +466,14 @@ private: if (index_ < static_cast(StaticId::STATIC_ID_END) || !destruct_guard_ok) return; #ifdef YOSYS_XTRACE_GET_PUT - if (yosys_xtrace) { - log("#X# PUT '%s' (index %d, refcount %u)\n", global_id_storage_.at(index_), index_, global_refcount_storage_.at(index_)); - } + if (yosys_xtrace) + log("#X# PUT '%s' (index %d, refcount %u)\n", global_id_storage_.at(index_), index_, refcount(index_)); #endif - --global_refcount_storage_[index_]; + auto it = global_refcount_storage_.find(index_); + log_assert(it != global_refcount_storage_.end() && it->second >= 1); + if (--it->second == 0) { + global_refcount_storage_.erase(it); + } #endif } };