From 4b79d23d000b42174614004c21c632b78fba780c Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 5 Feb 2022 16:04:48 +0000 Subject: [PATCH] Replace SenTreeSet with generic collection Introduce VNRef that can be used to wrap AstNode keys in STL collections, resulting in equality comparisons rather than identity comparisons. This can then replace the SenTreeSet data-structure. --- src/V3Active.cpp | 39 ++++++++++------------------ src/V3Ast.h | 41 +++++++++++++++++++++++++++++ src/V3Hasher.cpp | 8 ++++++ src/V3SenTree.h | 67 ++++++++++-------------------------------------- 4 files changed, 76 insertions(+), 79 deletions(-) diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 84f6f26e9..043f05233 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -32,9 +32,7 @@ #include "V3Global.h" #include "V3Active.h" #include "V3Ast.h" -#include "V3EmitCBase.h" #include "V3Const.h" -#include "V3SenTree.h" // for SenTreeSet #include "V3Graph.h" #include @@ -211,9 +209,8 @@ private: AstActive* m_iActivep = nullptr; // For current scope, the IActive we're building AstActive* m_cActivep = nullptr; // For current scope, the SActive(combo) we're building - SenTreeSet m_activeSens; // Sen lists for each active we've made - using ActiveMap = std::unordered_map; - ActiveMap m_activeMap; // Map sentree to active, for folding. + // Map from AstSenTree (equivalence) to the corresponding AstActive created. + std::unordered_map, AstActive*> m_activeMap; // METHODS void addActive(AstActive* nodep) { @@ -225,7 +222,6 @@ private: m_scopep = nodep; m_iActivep = nullptr; m_cActivep = nullptr; - m_activeSens.clear(); m_activeMap.clear(); iterateChildren(nodep); // Don't clear scopep, the namer persists beyond this visit @@ -259,29 +255,20 @@ public: } return m_iActivep; } + + // Return an AstActive that is sensitive to a SenTree equivalent to the given sentreep. AstActive* getActive(FileLine* fl, AstSenTree* sensesp) { - // Return a sentree in this scope that matches given sense list. - AstActive* activep = nullptr; - AstSenTree* const activeSenp = m_activeSens.find(sensesp); - if (activeSenp) { - const auto it = m_activeMap.find(activeSenp); - UASSERT(it != m_activeMap.end(), "Corrupt active map"); - activep = it->second; - } + auto it = m_activeMap.find(*sensesp); + // If found matching AstActive, return it + if (it != m_activeMap.end()) return it->second; - // Not found, form a new one - if (!activep) { - AstSenTree* const newsenp = sensesp->cloneTree(false); - activep = new AstActive(fl, "sequent", newsenp); - activep->sensesStorep(activep->sensesp()); - UINFO(8, " New ACTIVE " << activep << endl); - // Form the sensitivity list - addActive(activep); - m_activeMap[newsenp] = activep; - m_activeSens.add(newsenp); - // Note actives may have also been added above in the Active visitor - } + // No such AstActive yet, creat it, and add to map. + AstSenTree* const newsenp = sensesp->cloneTree(false); + AstActive* const activep = new AstActive(fl, "sequent", newsenp); + activep->sensesStorep(activep->sensesp()); + addActive(activep); + m_activeMap.emplace(*newsenp, activep); return activep; } diff --git a/src/V3Ast.h b/src/V3Ast.h index d6f9b230a..594749a0f 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2043,6 +2043,47 @@ inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) { inline void VNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); } //###################################################################### + +// VNRef is std::reference_wrapper that can only hold AstNode subtypes +template // +class VNRef final : public std::reference_wrapper { + static_assert(std::is_base_of::value, + "Type parameter 'T_Node' must be a subtype of AstNode"); + +public: + template + VNRef(U&& x) + : std::reference_wrapper{x} {} + + VNRef(const VNRef& other) noexcept + : std::reference_wrapper{other} {} +}; + +static_assert(sizeof(VNRef) == sizeof(std::reference_wrapper), + "VNRef should not contain extra members"); + +// Specializations of std::hash and std::equal_to for VNRef. This in turn +// enables us to use for example std::unordered_set> for +// sets using equality (AstNode::sameTree) rather than identity comparisons, +// without having to copy nodes into the collections. + +// Forward declaration to avoid including V3Hasher.h which needs V3Ast.h (this file). +size_t V3HasherUncachedHash(AstNode&); + +// Specialization of std::hash for VNRef +template // +struct std::hash> final { + size_t operator()(VNRef r) const { return V3HasherUncachedHash(r); } +}; + +// Specialization of std::equal_to for VNRef +template // +struct std::equal_to> final { + size_t operator()(VNRef ra, VNRef rb) const { + return ra.get().sameTree(&(rb.get())); + } +}; + //###################################################################### //=== AstNode* : Derived generic node types diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index 22a8181db..87bb8119e 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -520,3 +520,11 @@ V3Hash V3Hasher::uncachedHash(const AstNode* nodep) { const HasherVisitor visitor{nodep, HasherVisitor::Uncached{}}; return visitor.finalHash(); } + +//###################################################################### +// This is used by the std::hash specialization for VNRef. +// Declared separately to avoid a circular header dependency. + +size_t V3HasherUncachedHash(AstNode& node) { + return static_cast(V3Hasher::uncachedHash(&node).value()); +} diff --git a/src/V3SenTree.h b/src/V3SenTree.h index 24586dd46..c140580a0 100644 --- a/src/V3SenTree.h +++ b/src/V3SenTree.h @@ -31,51 +31,11 @@ // Collect SenTrees under the entire scope // And provide functions to find/add a new one -class SenTreeSet final { - // Hash table of sensitive blocks. -private: - // TYPES - struct HashSenTree { - size_t operator()(const AstSenTree* kp) const { - return V3Hasher::uncachedHash(kp).value(); - } - }; - - struct EqSenTree { - bool operator()(const AstSenTree* ap, const AstSenTree* bp) const { - return ap->sameTree(bp); - } - }; - - // MEMBERS - using Set = std::unordered_set; - Set m_trees; // Set of sensitive blocks, for folding. - -public: - // CONSTRUCTORS - SenTreeSet() = default; - - // METHODS - void add(AstSenTree* nodep) { m_trees.insert(nodep); } - - AstSenTree* find(AstSenTree* likep) { - AstSenTree* resultp = nullptr; - const auto it = m_trees.find(likep); - if (it != m_trees.end()) resultp = *it; - return resultp; - } - - void clear() { m_trees.clear(); } - -private: - VL_UNCOPYABLE(SenTreeSet); -}; - class SenTreeFinder final { private: // STATE AstTopScope* const m_topScopep; // Top scope to add global SenTrees to - SenTreeSet m_trees; // Set of global SenTrees + std::unordered_set> m_trees; // Set of global SenTrees VL_UNCOPYABLE(SenTreeFinder); @@ -87,25 +47,26 @@ public: explicit SenTreeFinder(AstNetlist* netlistp) : m_topScopep{netlistp->topScopep()} { // Gather existing global SenTrees - for (AstNode* nodep = m_topScopep->senTreesp(); nodep; nodep = nodep->nextp()) { - m_trees.add(VN_AS(nodep, SenTree)); + for (AstSenTree* senTreep = m_topScopep->senTreesp(); senTreep; + senTreep = VN_AS(senTreep->nextp(), SenTree)) { + m_trees.emplace(*senTreep); } } // METHODS - // Return a global AstSenTree that matches given SenTree. + // Return a global AstSenTree equivalent to the given senTreep. // If no such global AstSenTree exists create one and add it to the stored AstTopScope. AstSenTree* getSenTree(AstSenTree* senTreep) { - AstSenTree* treep = m_trees.find(senTreep); - if (!treep) { - // Not found, form a new one - treep = senTreep->cloneTree(false); - m_topScopep->addSenTreep(treep); - UINFO(8, " New SENTREE " << treep << endl); - m_trees.add(treep); - } - return treep; + auto it = m_trees.find(*senTreep); + // If match found, return it. + if (it != m_trees.end()) return &(*it).get(); + + // Not found, create a new one + AstSenTree* const newSenTreep = senTreep->cloneTree(false); + m_topScopep->addSenTreep(newSenTreep); + m_trees.emplace(*newSenTreep); + return newSenTreep; } // Return the global combinational AstSenTree.