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.
This commit is contained in:
Geza Lore 2022-02-05 16:04:48 +00:00
parent 77fe7c426e
commit 4b79d23d00
4 changed files with 76 additions and 79 deletions

View File

@ -32,9 +32,7 @@
#include "V3Global.h" #include "V3Global.h"
#include "V3Active.h" #include "V3Active.h"
#include "V3Ast.h" #include "V3Ast.h"
#include "V3EmitCBase.h"
#include "V3Const.h" #include "V3Const.h"
#include "V3SenTree.h" // for SenTreeSet
#include "V3Graph.h" #include "V3Graph.h"
#include <unordered_map> #include <unordered_map>
@ -211,9 +209,8 @@ private:
AstActive* m_iActivep = nullptr; // For current scope, the IActive we're building 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 AstActive* m_cActivep = nullptr; // For current scope, the SActive(combo) we're building
SenTreeSet m_activeSens; // Sen lists for each active we've made // Map from AstSenTree (equivalence) to the corresponding AstActive created.
using ActiveMap = std::unordered_map<AstSenTree*, AstActive*>; std::unordered_map<VNRef<AstSenTree>, AstActive*> m_activeMap;
ActiveMap m_activeMap; // Map sentree to active, for folding.
// METHODS // METHODS
void addActive(AstActive* nodep) { void addActive(AstActive* nodep) {
@ -225,7 +222,6 @@ private:
m_scopep = nodep; m_scopep = nodep;
m_iActivep = nullptr; m_iActivep = nullptr;
m_cActivep = nullptr; m_cActivep = nullptr;
m_activeSens.clear();
m_activeMap.clear(); m_activeMap.clear();
iterateChildren(nodep); iterateChildren(nodep);
// Don't clear scopep, the namer persists beyond this visit // Don't clear scopep, the namer persists beyond this visit
@ -259,29 +255,20 @@ public:
} }
return m_iActivep; return m_iActivep;
} }
// Return an AstActive that is sensitive to a SenTree equivalent to the given sentreep.
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) { AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
// Return a sentree in this scope that matches given sense list.
AstActive* activep = nullptr; auto it = m_activeMap.find(*sensesp);
AstSenTree* const activeSenp = m_activeSens.find(sensesp); // If found matching AstActive, return it
if (activeSenp) { if (it != m_activeMap.end()) return it->second;
const auto it = m_activeMap.find(activeSenp);
UASSERT(it != m_activeMap.end(), "Corrupt active map");
activep = it->second;
}
// Not found, form a new one // No such AstActive yet, creat it, and add to map.
if (!activep) {
AstSenTree* const newsenp = sensesp->cloneTree(false); AstSenTree* const newsenp = sensesp->cloneTree(false);
activep = new AstActive(fl, "sequent", newsenp); AstActive* const activep = new AstActive(fl, "sequent", newsenp);
activep->sensesStorep(activep->sensesp()); activep->sensesStorep(activep->sensesp());
UINFO(8, " New ACTIVE " << activep << endl);
// Form the sensitivity list
addActive(activep); addActive(activep);
m_activeMap[newsenp] = activep; m_activeMap.emplace(*newsenp, activep);
m_activeSens.add(newsenp);
// Note actives may have also been added above in the Active visitor
}
return activep; return activep;
} }

View File

@ -2043,6 +2043,47 @@ inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) {
inline void VNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); } inline void VNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
//###################################################################### //######################################################################
// VNRef is std::reference_wrapper that can only hold AstNode subtypes
template <typename T_Node> //
class VNRef final : public std::reference_wrapper<T_Node> {
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
public:
template <typename U>
VNRef(U&& x)
: std::reference_wrapper<T_Node>{x} {}
VNRef(const VNRef& other) noexcept
: std::reference_wrapper<T_Node>{other} {}
};
static_assert(sizeof(VNRef<AstNode>) == sizeof(std::reference_wrapper<AstNode>),
"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<VNRef<AstNode>> 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 <typename T_Node> //
struct std::hash<VNRef<T_Node>> final {
size_t operator()(VNRef<T_Node> r) const { return V3HasherUncachedHash(r); }
};
// Specialization of std::equal_to for VNRef
template <typename T_Node> //
struct std::equal_to<VNRef<T_Node>> final {
size_t operator()(VNRef<T_Node> ra, VNRef<T_Node> rb) const {
return ra.get().sameTree(&(rb.get()));
}
};
//###################################################################### //######################################################################
//=== AstNode* : Derived generic node types //=== AstNode* : Derived generic node types

View File

@ -520,3 +520,11 @@ V3Hash V3Hasher::uncachedHash(const AstNode* nodep) {
const HasherVisitor visitor{nodep, HasherVisitor::Uncached{}}; const HasherVisitor visitor{nodep, HasherVisitor::Uncached{}};
return visitor.finalHash(); 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<size_t>(V3Hasher::uncachedHash(&node).value());
}

View File

@ -31,51 +31,11 @@
// Collect SenTrees under the entire scope // Collect SenTrees under the entire scope
// And provide functions to find/add a new one // 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<AstSenTree*, HashSenTree, EqSenTree>;
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 { class SenTreeFinder final {
private: private:
// STATE // STATE
AstTopScope* const m_topScopep; // Top scope to add global SenTrees to AstTopScope* const m_topScopep; // Top scope to add global SenTrees to
SenTreeSet m_trees; // Set of global SenTrees std::unordered_set<VNRef<AstSenTree>> m_trees; // Set of global SenTrees
VL_UNCOPYABLE(SenTreeFinder); VL_UNCOPYABLE(SenTreeFinder);
@ -87,25 +47,26 @@ public:
explicit SenTreeFinder(AstNetlist* netlistp) explicit SenTreeFinder(AstNetlist* netlistp)
: m_topScopep{netlistp->topScopep()} { : m_topScopep{netlistp->topScopep()} {
// Gather existing global SenTrees // Gather existing global SenTrees
for (AstNode* nodep = m_topScopep->senTreesp(); nodep; nodep = nodep->nextp()) { for (AstSenTree* senTreep = m_topScopep->senTreesp(); senTreep;
m_trees.add(VN_AS(nodep, SenTree)); senTreep = VN_AS(senTreep->nextp(), SenTree)) {
m_trees.emplace(*senTreep);
} }
} }
// METHODS // 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. // If no such global AstSenTree exists create one and add it to the stored AstTopScope.
AstSenTree* getSenTree(AstSenTree* senTreep) { AstSenTree* getSenTree(AstSenTree* senTreep) {
AstSenTree* treep = m_trees.find(senTreep); auto it = m_trees.find(*senTreep);
if (!treep) { // If match found, return it.
// Not found, form a new one if (it != m_trees.end()) return &(*it).get();
treep = senTreep->cloneTree(false);
m_topScopep->addSenTreep(treep); // Not found, create a new one
UINFO(8, " New SENTREE " << treep << endl); AstSenTree* const newSenTreep = senTreep->cloneTree(false);
m_trees.add(treep); m_topScopep->addSenTreep(newSenTreep);
} m_trees.emplace(*newSenTreep);
return treep; return newSenTreep;
} }
// Return the global combinational AstSenTree. // Return the global combinational AstSenTree.