diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index f6f02249d..861383a57 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -181,6 +181,7 @@ RAW_OBJS = \ V3Depth.o \ V3DepthBlock.o \ V3Descope.o \ + V3DupFinder.o \ V3EmitC.o \ V3EmitCInlines.o \ V3EmitCSyms.o \ @@ -202,7 +203,7 @@ RAW_OBJS = \ V3GraphDfa.o \ V3GraphPathChecker.o \ V3GraphTest.o \ - V3Hashed.o \ + V3Hasher.o \ V3HierBlock.o \ V3Inline.o \ V3Inst.o \ diff --git a/src/V3Ast.h b/src/V3Ast.h index a758bf700..306f75b95 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1358,12 +1358,9 @@ public: } // Creating from raw data (sameHash functions) V3Hash() { setBoth(1, 0); } - // cppcheck-suppress noExplicitConstructor - V3Hash(uint32_t val) { setBoth(1, val); } - // cppcheck-suppress noExplicitConstructor - V3Hash(const void* vp) { setBoth(1, cvtToHash(vp)); } - // cppcheck-suppress noExplicitConstructor - V3Hash(const string& name); + explicit V3Hash(uint32_t val) { setBoth(1, val); } + explicit V3Hash(const void* vp) { setBoth(1, cvtToHash(vp)); } + explicit V3Hash(const string& name); V3Hash(V3Hash h1, V3Hash h2) { setBoth(1, h1.hshval() * 31 + h2.hshval()); } V3Hash(V3Hash h1, V3Hash h2, V3Hash h3) { setBoth(1, (h1.hshval() * 31 + h2.hshval()) * 31 + h3.hshval()); diff --git a/src/V3Combine.cpp b/src/V3Combine.cpp index 1ae206e7a..4fc8f5e6c 100644 --- a/src/V3Combine.cpp +++ b/src/V3Combine.cpp @@ -24,7 +24,7 @@ #include "V3Global.h" #include "V3Combine.h" -#include "V3Hashed.h" +#include "V3DupFinder.h" #include "V3Stats.h" #include "V3Ast.h" @@ -114,16 +114,16 @@ private: // NODE STATE // Entire netlist: AstUser3InUse m_user3InUse; // Marks replaced AstCFuncs - // AstUser4InUse part of V3Hashed + // AstUser4InUse part of V3Hasher in V3DupFinder // STATE VDouble0 m_cfuncsCombined; // Statistic tracking CombCallVisitor m_call; // Tracking of function call users - V3Hashed m_hashed; // Hash for every CFunc in module + V3DupFinder m_dupFinder; // Duplicate finder for CFuncs in module // METHODS void walkEmptyFuncs() { - for (const auto& itr : m_hashed) { + for (const auto& itr : m_dupFinder) { AstCFunc* const oldfuncp = VN_CAST(itr.second, CFunc); UASSERT_OBJ(oldfuncp, itr.second, "Not a CFunc in hash"); if (!oldfuncp->emptyBody()) continue; @@ -144,14 +144,14 @@ private: void walkDupFuncs() { // Do non-slow first as then favors naming functions based on fast name for (const bool slow : {false, true}) { - for (auto newIt = m_hashed.begin(); newIt != m_hashed.end(); ++newIt) { + for (auto newIt = m_dupFinder.begin(); newIt != m_dupFinder.end(); ++newIt) { AstCFunc* const newfuncp = VN_CAST(newIt->second, CFunc); UASSERT_OBJ(newfuncp, newIt->second, "Not a CFunc in hash"); if (newfuncp->user3()) continue; // Already replaced if (newfuncp->slow() != slow) continue; auto oldIt = newIt; ++oldIt; // Skip over current position - for (; oldIt != m_hashed.end(); ++oldIt) { + for (; oldIt != m_dupFinder.end(); ++oldIt) { AstCFunc* const oldfuncp = VN_CAST(oldIt->second, CFunc); UASSERT_OBJ(oldfuncp, oldIt->second, "Not a CFunc in hash"); UASSERT_OBJ(newfuncp != oldfuncp, newfuncp, @@ -184,10 +184,10 @@ private: } virtual void visit(AstNodeModule* nodep) override { UINFO(4, " MOD " << nodep << endl); - m_hashed.clear(); + m_dupFinder.clear(); // Compute hash of all CFuncs in the module iterateChildren(nodep); - if (debug() >= 9) m_hashed.dumpFilePrefixed("combine"); + if (debug() >= 9) m_dupFinder.dumpFilePrefixed("combine"); // Walk the hashes removing empty functions walkEmptyFuncs(); // Walk the hashes looking for duplicate functions @@ -196,7 +196,7 @@ private: virtual void visit(AstCFunc* nodep) override { if (nodep->dontCombine()) return; // Hash the entire function - m_hashed.hashAndInsert(nodep); + m_dupFinder.insert(nodep); } //-------------------- diff --git a/src/V3CoverageJoin.cpp b/src/V3CoverageJoin.cpp index 48cf67fad..f0f30c098 100644 --- a/src/V3CoverageJoin.cpp +++ b/src/V3CoverageJoin.cpp @@ -22,7 +22,7 @@ #include "V3Global.h" #include "V3CoverageJoin.h" -#include "V3Hashed.h" +#include "V3DupFinder.h" #include "V3Stats.h" #include @@ -33,10 +33,7 @@ class CoverageJoinVisitor final : public AstNVisitor { private: // NODE STATE - // V3Hashed - // AstCoverToggle->VarRef::user4() // V3Hashed calculation - - // AstUser4InUse In V3Hashed + // AstUser4InUse In V3Hasher via V3DupFinder // STATE std::vector m_toggleps; // List of of all AstCoverToggle's @@ -49,9 +46,9 @@ private: void detectDuplicates() { UINFO(9, "Finding duplicates\n"); // Note uses user4 - V3Hashed hashed; // Duplicate code detection + V3DupFinder dupFinder; // Duplicate code detection // Hash all of the original signals we toggle cover - for (AstCoverToggle* nodep : m_toggleps) hashed.hashAndInsert(nodep->origp()); + for (AstCoverToggle* nodep : m_toggleps) dupFinder.insert(nodep->origp()); // Find if there are any duplicates for (AstCoverToggle* nodep : m_toggleps) { // nodep->backp() is null if we already detected it's a duplicate and unlinked it. @@ -60,10 +57,10 @@ private: // This prevents making chains where a->b, then c->d, then b->c, as we'll // find a->b, a->c, a->d directly. while (true) { - const auto dupit = hashed.findDuplicate(nodep->origp()); - if (dupit == hashed.end()) break; + const auto dupit = dupFinder.findDuplicate(nodep->origp()); + if (dupit == dupFinder.end()) break; // - AstNode* duporigp = hashed.iteratorNodep(dupit); + AstNode* duporigp = dupit->second; // Note hashed will point to the original variable (what's // duplicated), not the covertoggle, but we need to get back to the // covertoggle which is immediately above, so: @@ -82,7 +79,7 @@ private: removep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(removep), removep); // Remove node from comparison so don't hit it again - hashed.erase(dupit); + dupFinder.erase(dupit); ++m_statToggleJoins; } } diff --git a/src/V3DupFinder.cpp b/src/V3DupFinder.cpp new file mode 100644 index 000000000..e6aa43d36 --- /dev/null +++ b/src/V3DupFinder.cpp @@ -0,0 +1,97 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Hashed common code into functions +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 by Wilson Snyder. This program is free software; you +// can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Global.h" +#include "V3DupFinder.h" +#include "V3Ast.h" +#include "V3File.h" + +#include +#include +#include +#include + +//###################################################################### +// V3DupFinder class functions + +bool V3DupFinder::sameNodes(AstNode* node1p, AstNode* node2p) { + return m_hasher(node1p) == m_hasher(node2p) // Same hash + && node1p->sameTree(node2p); // Same tree +} + +V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp) { + const auto& er = equal_range(m_hasher(nodep)); + for (iterator it = er.first; it != er.second; ++it) { + AstNode* const node2p = it->second; + if (nodep == node2p) continue; // Same node is not a duplicate + if (checkp && !checkp->isSame(nodep, node2p)) continue; // User says it is not a duplicate + if (!nodep->sameTree(node2p)) continue; // Not the same trees + // Found duplicate! + return it; + } + return end(); +} + +void V3DupFinder::dumpFile(const string& filename, bool tree) { + const std::unique_ptr logp(V3File::new_ofstream(filename)); + if (logp->fail()) v3fatal("Can't write " << filename); + + std::unordered_map dist; + + V3Hash lasthash; + int num_in_bucket = 0; + for (auto it = cbegin(); true; ++it) { + if (it == cend() || lasthash != it->first) { + if (it != cend()) lasthash = it->first; + if (num_in_bucket) { + if (dist.find(num_in_bucket) == dist.end()) { + dist.emplace(num_in_bucket, 1); + } else { + ++dist[num_in_bucket]; + } + } + num_in_bucket = 0; + } + if (it == cend()) break; + num_in_bucket++; + } + *logp << "\n*** STATS:\n\n"; + *logp << " #InBucket Occurrences\n"; + for (const auto& i : dist) { + *logp << " " << std::setw(9) << i.first << " " << std::setw(12) << i.second << '\n'; + } + + *logp << "\n*** Dump:\n\n"; + for (const auto& it : *this) { + if (lasthash != it.first) { + lasthash = it.first; + *logp << " " << it.first << '\n'; + } + *logp << "\t" << it.second << '\n'; + // Dumping the entire tree may make nearly N^2 sized dumps, + // because the nodes under this one may also be in the hash table! + if (tree) it.second->dumpTree(*logp, " "); + } +} + +void V3DupFinder::dumpFilePrefixed(const string& nameComment, bool tree) { + if (v3Global.opt.dumpTree()) { // + dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree); + } +} diff --git a/src/V3DupFinder.h b/src/V3DupFinder.h new file mode 100644 index 000000000..43a63be0d --- /dev/null +++ b/src/V3DupFinder.h @@ -0,0 +1,82 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Hash AST trees to find duplicates +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2005-2021 by Wilson Snyder. This program is free software; you +// can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +// +// Datastructure for finding duplicate AstNode trees via hashing +// +//************************************************************************* + +#ifndef VERILATOR_V3DUPFINDER_H_ +#define VERILATOR_V3DUPFINDER_H_ +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Error.h" +#include "V3Ast.h" +#include "V3Hasher.h" + +#include + +//============================================================================ + +struct V3DupFinderUserSame { + // Functor for V3DupFinder::findDuplicate + virtual bool isSame(AstNode*, AstNode*) = 0; + V3DupFinderUserSame() = default; + virtual ~V3DupFinderUserSame() = default; +}; + +// This really is just a multimap from 'node hash' to 'node pointer', with some minor extensions. +class V3DupFinder final : private std::multimap { + using Super = std::multimap; + + // MEMBERS + const V3Hasher m_hasher; + +public: + // CONSTRUCTORS + V3DupFinder(){}; + ~V3DupFinder() = default; + + // METHODS + VL_DEBUG_FUNC; // Declare debug() + + // Expose minimal set of superclass interface + using Super::begin; + using Super::cbegin; + using Super::cend; + using Super::clear; + using Super::const_iterator; + using Super::empty; + using Super::end; + using Super::erase; + using Super::iterator; + + // Insert node into data structure + iterator insert(AstNode* nodep) { return emplace(m_hasher(nodep), nodep); } + + // Check if nodes are the same (same as node1p->sameTree(node2p), + // but first checks the hashes are equal for speed) + bool sameNodes(AstNode* node1p, AstNode* node2p); + + // Return duplicate, if one was inserted, with optional user check for sameness + iterator findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp = nullptr); + + // Dump for debug + void dumpFile(const string& filename, bool tree); + void dumpFilePrefixed(const string& nameComment, bool tree = false); +}; + +#endif // Guard diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index 913c6c645..a23c89485 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -30,7 +30,7 @@ #include "V3Graph.h" #include "V3Const.h" #include "V3Stats.h" -#include "V3Hashed.h" +#include "V3DupFinder.h" #include #include @@ -900,7 +900,7 @@ void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* //###################################################################### // Auxiliary hash class for GateDedupeVarVisitor -class GateDedupeHash final : public V3HashedUserSame { +class GateDedupeHash final : public V3DupFinderUserSame { private: // NODE STATE // Ast*::user2p -> parent AstNodeAssign* for this rhsp @@ -911,22 +911,22 @@ private: // AstUser1InUse m_inuser1; (Allocated for use in GateVisitor) // AstUser2InUse m_inuser2; (Allocated for use in GateVisitor) AstUser3InUse m_inuser3; - // AstUser4InUse m_inuser4; (Allocated for use in V3Hashed) + // AstUser4InUse m_inuser4; (Allocated for use in V3Hasher via V3DupFinder) AstUser5InUse m_inuser5; - V3Hashed m_hashed; // Hash, contains rhs of assigns + V3DupFinder m_dupFinder; // Duplicate finder for rhs of assigns std::unordered_set m_nodeDeleteds; // Any node in this hash was deleted VL_DEBUG_FUNC; // Declare debug() - void hash(AstNode* nodep) { - // !nullptr && the object is hashable - if (nodep && !nodep->sameHash().isIllegal()) m_hashed.hash(nodep); - } bool sameHash(AstNode* node1p, AstNode* node2p) { - return (node1p && node2p && !node1p->sameHash().isIllegal() - && !node2p->sameHash().isIllegal() && m_hashed.sameNodes(node1p, node2p)); + return node1p // + && node2p // + && !node1p->sameHash().isIllegal() // + && !node2p->sameHash().isIllegal() // + && m_dupFinder.sameNodes(node1p, node2p); } + bool same(AstNode* node1p, AstNode* node2p) { return node1p == node2p || sameHash(node1p, node2p); } @@ -958,7 +958,7 @@ public: || (extra2p && m_nodeDeleteds.find(extra2p) != m_nodeDeleteds.end())); } - // Callback from V3Hashed::findDuplicate + // Callback from V3DupFinder::findDuplicate virtual bool isSame(AstNode* node1p, AstNode* node2p) override { // Assignment may have been hashReplaced, if so consider non-match (effectively removed) if (isReplaced(node1p) || isReplaced(node2p)) { @@ -977,25 +977,21 @@ public: rhsp->user3p(extra1p); rhsp->user5p(extra2p); - hash(extra1p); - hash(extra2p); - - const auto inserted = m_hashed.hashAndInsert(rhsp); - const auto dupit = m_hashed.findDuplicate(rhsp, this); - // Even though rhsp was just inserted, V3Hashed::findDuplicate doesn't - // return anything in the hash that has the same pointer (V3Hashed.cpp::findDuplicate) + const auto inserted = m_dupFinder.insert(rhsp); + const auto dupit = m_dupFinder.findDuplicate(rhsp, this); + // Even though rhsp was just inserted, V3DupFinder::findDuplicate doesn't + // return anything in the hash that has the same pointer (V3DupFinder::findDuplicate) // So dupit is either a different, duplicate rhsp, or the end of the hash. - if (dupit != m_hashed.end()) { - m_hashed.erase(inserted); - return VN_CAST(m_hashed.iteratorNodep(dupit)->user2p(), NodeAssign); + if (dupit != m_dupFinder.end()) { + m_dupFinder.erase(inserted); + return VN_CAST(dupit->second->user2p(), NodeAssign); } // Retain new inserted information return nullptr; } void check() { - m_hashed.check(); - for (const auto& itr : m_hashed) { + for (const auto& itr : m_dupFinder) { AstNode* nodep = itr.second; AstNode* activep = nodep->user3p(); AstNode* condVarp = nodep->user5p(); @@ -1003,9 +999,9 @@ public: // This class won't break if activep isn't an active, or // ifVar isn't a var, but this is checking the caller's construction. UASSERT_OBJ(!activep || (!VN_DELETED(activep) && VN_IS(activep, Active)), nodep, - "V3Hashed check failed, lost active pointer"); + "V3DupFinder check failed, lost active pointer"); UASSERT_OBJ(!condVarp || !VN_DELETED(condVarp), nodep, - "V3Hashed check failed, lost if pointer"); + "V3DupFinder check failed, lost if pointer"); } } } diff --git a/src/V3Hashed.cpp b/src/V3Hashed.cpp deleted file mode 100644 index acfe9ba94..000000000 --- a/src/V3Hashed.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Hashed common code into functions -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// Copyright 2003-2021 by Wilson Snyder. This program is free software; you -// can redistribute it and/or modify it under the terms of either the GNU -// Lesser General Public License Version 3 or the Perl Artistic License -// Version 2.0. -// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -// -//************************************************************************* -// V3Hashed's Transformations: -// -// Hash each node depth first -// Hash includes varp name and operator type, and constants -// Form lookup table based on hash of each statement w/ nodep and next nodep -// -//************************************************************************* - -#include "config_build.h" -#include "verilatedos.h" - -#include "V3Global.h" -#include "V3Hashed.h" -#include "V3Ast.h" -#include "V3File.h" - -#include -#include -#include -#include - -//###################################################################### -// Hashed state, as a visitor of each AstNode - -class HashedVisitor final : public AstNVisitor { -private: - // NODE STATE - // Entire netlist: - // AstNodeStmt::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal) - // AstUser4InUse in V3Hashed.h - - // STATE - V3Hash m_lowerHash; // Hash of the statement we're building - bool m_cacheInUser4; // Use user4 to cache each V3Hash? - - // METHODS - VL_DEBUG_FUNC; // Declare debug() - - void nodeHashIterate(AstNode* nodep) { - V3Hash thisHash; - if (!m_cacheInUser4 || !nodep->user4()) { - UASSERT_OBJ( - !(VN_IS(nodep->backp(), CFunc) - && !(VN_IS(nodep, NodeStmt) || VN_IS(nodep, CFunc))), - nodep, - "Node " << nodep->prettyTypeName() - << " in statement position but not marked stmt (node under function)"); - VL_RESTORER(m_lowerHash); - { - m_lowerHash = nodep->sameHash(); - UASSERT_OBJ(!m_lowerHash.isIllegal(), nodep, - "sameHash function undefined (returns 0) for node under CFunc."); - // For identical nodes, the type should be the same thus - // dtypep should be the same too - m_lowerHash - = V3Hash(m_lowerHash, V3Hash(nodep->type() << 6, V3Hash(nodep->dtypep()))); - // Now update m_lowerHash for our children's (and next children) contributions - iterateChildren(nodep); - // Store the hash value - nodep->user4(m_lowerHash.fullValue()); - // UINFO(9, " hashnode "<(nodep)); - } - V3Hash finalHash() const { return m_lowerHash; } - virtual ~HashedVisitor() override = default; -}; - -//###################################################################### -// Hashed class functions - -V3Hash V3Hashed::uncachedHash(const AstNode* nodep) { - HashedVisitor visitor(nodep); - return visitor.finalHash(); -} - -V3Hashed::iterator V3Hashed::hashAndInsert(AstNode* nodep) { - hash(nodep); - return m_hashMmap.emplace(nodeHash(nodep), nodep); -} - -void V3Hashed::hash(AstNode* nodep) { - UINFO(8, " hashI " << nodep << endl); - if (!nodep->user4p()) { HashedVisitor visitor(nodep); } -} - -bool V3Hashed::sameNodes(AstNode* node1p, AstNode* node2p) { - UASSERT_OBJ(node1p->user4p(), node1p, "Called isIdentical on non-hashed nodes"); - UASSERT_OBJ(node2p->user4p(), node2p, "Called isIdentical on non-hashed nodes"); - return (node1p->user4p() == node2p->user4p() // Same hash - && node1p->sameTree(node2p)); -} - -void V3Hashed::erase(iterator it) { - AstNode* nodep = iteratorNodep(it); - UINFO(8, " erase " << nodep << endl); - UASSERT_OBJ(nodep->user4p(), nodep, "Called removeNode on non-hashed node"); - m_hashMmap.erase(it); - nodep->user4p(nullptr); // So we don't allow removeNode again -} - -void V3Hashed::check() { - for (const auto& itr : *this) { - AstNode* nodep = itr.second; - UASSERT_OBJ(nodep->user4p(), nodep, "V3Hashed check failed, non-hashed node"); - } -} - -void V3Hashed::dumpFilePrefixed(const string& nameComment, bool tree) { - if (v3Global.opt.dumpTree()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree); -} - -void V3Hashed::dumpFile(const string& filename, bool tree) { - const std::unique_ptr logp(V3File::new_ofstream(filename)); - if (logp->fail()) v3fatal("Can't write " << filename); - - std::unordered_map dist; - - V3Hash lasthash; - int num_in_bucket = 0; - for (HashMmap::iterator it = begin(); true; ++it) { - if (it == end() || lasthash != it->first) { - if (it != end()) lasthash = it->first; - if (num_in_bucket) { - if (dist.find(num_in_bucket) == dist.end()) { - dist.emplace(num_in_bucket, 1); - } else { - ++dist[num_in_bucket]; - } - } - num_in_bucket = 0; - } - if (it == end()) break; - num_in_bucket++; - } - *logp << "\n*** STATS:\n\n"; - *logp << " #InBucket Occurrences\n"; - for (const auto& i : dist) { - *logp << " " << std::setw(9) << i.first << " " << std::setw(12) << i.second << '\n'; - } - - *logp << "\n*** Dump:\n\n"; - for (const auto& itr : *this) { - if (lasthash != itr.first) { - lasthash = itr.first; - *logp << " " << itr.first << '\n'; - } - *logp << "\t" << itr.second << '\n'; - // Dumping the entire tree may make nearly N^2 sized dumps, - // because the nodes under this one may also be in the hash table! - if (tree) itr.second->dumpTree(*logp, " "); - } -} - -V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep, V3HashedUserSame* checkp) { - UINFO(8, " findD " << nodep << endl); - UASSERT_OBJ(nodep->user4p(), nodep, "Called findDuplicate on non-hashed node"); - std::pair eqrange - = mmap().equal_range(nodeHash(nodep)); - for (HashMmap::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) { - AstNode* node2p = eqit->second; - if (nodep != node2p && (!checkp || checkp->isSame(nodep, node2p)) - && sameNodes(nodep, node2p)) { - return eqit; - } - } - return end(); -} diff --git a/src/V3Hashed.h b/src/V3Hashed.h deleted file mode 100644 index 07bdcb7d4..000000000 --- a/src/V3Hashed.h +++ /dev/null @@ -1,92 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Hash AST trees to find duplicates -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// Copyright 2005-2021 by Wilson Snyder. This program is free software; you -// can redistribute it and/or modify it under the terms of either the GNU -// Lesser General Public License Version 3 or the Perl Artistic License -// Version 2.0. -// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -// -//************************************************************************* - -#ifndef VERILATOR_V3HASHED_H_ -#define VERILATOR_V3HASHED_H_ -#include "config_build.h" -#include "verilatedos.h" - -#include "V3Error.h" -#include "V3Ast.h" - -//============================================================================ - -class VHashedBase VL_NOT_FINAL { -public: - // CONSTRUCTORS - VHashedBase() = default; - ~VHashedBase() = default; - - // METHODS - VL_DEBUG_FUNC; // Declare debug() -}; - -//============================================================================ - -struct V3HashedUserSame { - // Functor for V3Hashed::findDuplicate - virtual bool isSame(AstNode*, AstNode*) = 0; - V3HashedUserSame() = default; - virtual ~V3HashedUserSame() = default; -}; - -class V3Hashed final : public VHashedBase { - // NODE STATE - // AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal) - AstUser4InUse m_inuser4; - - // TYPES -public: - using HashMmap = std::multimap; - using iterator = HashMmap::iterator; - -private: - // MEMBERS - HashMmap m_hashMmap; // hashvalue -> nodes with that hash - -public: - // CONSTRUCTORS - V3Hashed() { clear(); } - ~V3Hashed() = default; - - // ACCESSORS - HashMmap& mmap() { return m_hashMmap; } // Return map for iteration - iterator begin() { return m_hashMmap.begin(); } - iterator end() { return m_hashMmap.end(); } - - // METHODS - void clear() { - m_hashMmap.clear(); - AstNode::user4ClearTree(); - } - void check(); // Check assertions on structure - // Hash the node, and insert into map. Return iterator to inserted - iterator hashAndInsert(AstNode* nodep); - static void hash(AstNode* nodep); // Only hash the node - // After hashing, and tell if identical - static bool sameNodes(AstNode* node1p, AstNode* node2p); - void erase(iterator it); // Remove node from structures - // Return duplicate in hash, if any, with optional user check for sameness - iterator findDuplicate(AstNode* nodep, V3HashedUserSame* checkp = nullptr); - AstNode* iteratorNodep(iterator it) { return it->second; } - void dumpFile(const string& filename, bool tree); - void dumpFilePrefixed(const string& nameComment, bool tree = false); - static V3Hash nodeHash(AstNode* nodep) { return V3Hash(nodep->user4p()); } - // Hash of the nodep tree, without caching in user4. - static V3Hash uncachedHash(const AstNode* nodep); -}; - -#endif // Guard diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp new file mode 100644 index 000000000..4a8f51742 --- /dev/null +++ b/src/V3Hasher.cpp @@ -0,0 +1,91 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Hashed common code into functions +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 by Wilson Snyder. This program is free software; you +// can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Hasher.h" + +//###################################################################### +// Visitor that computes node hashes + +class HasherVisitor final : public AstNVisitor { +private: + // NODE STATE + // AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal) + // AstUser4InUse in V3Hasher.h + + // STATE + V3Hash m_lowerHash; // Hash of the statement we're building + const bool m_cacheInUser4; // Use user4 to cache each V3Hash? + + // METHODS + VL_DEBUG_FUNC; // Declare debug() + + //-------------------- + virtual void visit(AstVar*) override {} + virtual void visit(AstTypedef*) override {} + virtual void visit(AstParamTypeDType*) override {} + + virtual void visit(AstNode* nodep) override { + V3Hash thisHash; + if (!m_cacheInUser4 || !nodep->user4()) { + VL_RESTORER(m_lowerHash); + { + m_lowerHash = nodep->sameHash(); + UASSERT_OBJ(!m_lowerHash.isIllegal(), nodep, + "sameHash function undefined (returns 0) for node under CFunc."); + // For identical nodes, the type should be the same thus + // dtypep should be the same too + m_lowerHash = V3Hash(m_lowerHash, + V3Hash(V3Hash(nodep->type() << 6), V3Hash(nodep->dtypep()))); + // Now update m_lowerHash for our children's (and next children) contributions + iterateChildrenConst(nodep); + // Store the hash value + if (m_cacheInUser4) { nodep->user4(m_lowerHash.fullValue()); } + thisHash = m_lowerHash; + } + } + // Update what will become the above node's hash + m_lowerHash += m_cacheInUser4 ? V3Hash(nodep->user4()) : thisHash; + } + +public: + // CONSTRUCTORS + explicit HasherVisitor(AstNode* nodep) + : m_cacheInUser4{true} { + iterate(nodep); + } + explicit HasherVisitor(const AstNode* nodep) + : m_cacheInUser4{false} { + iterate(const_cast(nodep)); + } + V3Hash finalHash() const { return m_lowerHash; } + virtual ~HasherVisitor() override = default; +}; + +//###################################################################### +// V3Hasher methods + +V3Hash V3Hasher::operator()(AstNode* nodep) const { + if (!nodep->user4()) { HasherVisitor visitor(nodep); } + return V3Hash(nodep->user4()); +} + +V3Hash V3Hasher::uncachedHash(const AstNode* nodep) { + HasherVisitor visitor(nodep); + return visitor.finalHash(); +} diff --git a/src/V3Hasher.h b/src/V3Hasher.h new file mode 100644 index 000000000..fc90de27f --- /dev/null +++ b/src/V3Hasher.h @@ -0,0 +1,51 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Hash AST trees to find duplicates +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2005-2021 by Wilson Snyder. This program is free software; you +// can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +// +// V3Hasher handles computation of AstNode hashes +// +//************************************************************************* + +#ifndef VERILATOR_V3HASHER_H_ +#define VERILATOR_V3HASHER_H_ +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Error.h" +#include "V3Ast.h" + +//============================================================================ + +class V3Hasher final { + // NODE STATE + // AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal) + AstUser4InUse m_inuser4; + +public: + // CONSTRUCTORS + V3Hasher() = default; + ~V3Hasher() = default; + + // METHODS + VL_DEBUG_FUNC; // Declare debug() + + // Compute hash of node. This method caches the hash in the node's user4(). + V3Hash operator()(AstNode* nodep) const; + + // Compute hash of node, without caching in user4. + static V3Hash uncachedHash(const AstNode* nodep); +}; + +#endif // Guard diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 8b7ba30d6..a681572c0 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -56,7 +56,7 @@ #include "V3Parse.h" #include "V3Width.h" #include "V3Unroll.h" -#include "V3Hashed.h" +#include "V3Hasher.h" #include #include @@ -314,7 +314,7 @@ class ParamProcessor final { key += "[" + cvtToStr(bdtp->left()) + ":" + cvtToStr(bdtp->right()) + "]"; } } - V3Hash hash = V3Hashed::uncachedHash(nodep); + V3Hash hash = V3Hasher::uncachedHash(nodep); // Force hash collisions -- for testing only if (VL_UNLIKELY(v3Global.opt.debugCollision())) hash = V3Hash(); int num; diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index ebd47c626..82c2404ae 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -30,7 +30,7 @@ #include "V3Global.h" #include "V3Premit.h" #include "V3Ast.h" -#include "V3Hashed.h" +#include "V3DupFinder.h" #include "V3Stats.h" #include @@ -96,7 +96,7 @@ private: // *::user4() -> See PremitAssignVisitor AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; - // AstUser4InUse part of V3Hashed + // AstUser4InUse part of V3Hasher via V3DupFinder // STATE AstNodeModule* m_modp = nullptr; // Current module @@ -106,7 +106,7 @@ private: AstTraceInc* m_inTracep = nullptr; // Inside while loop, special statement additions bool m_assignLhs = false; // Inside assignment lhs, don't breakup extracts - V3Hashed m_hashed; // Hash set for static constants that can be reused + V3DupFinder m_dupFinder; // Duplicate finder for static constants that can be reused VDouble0 m_staticConstantsExtracted; // Statistic tracking VDouble0 m_staticConstantsReused; // Statistic tracking @@ -184,9 +184,8 @@ private: && !constp->num().isFourState(); if (useStatic) { // Extract as static constant - m_hashed.hash(constp); - const auto& it = m_hashed.findDuplicate(constp); - if (it == m_hashed.end()) { + const auto& it = m_dupFinder.findDuplicate(constp); + if (it == m_dupFinder.end()) { const string newvarname = string("__Vconst") + cvtToStr(m_modp->varNumGetInc()); varp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname, nodep->dtypep()); @@ -194,7 +193,7 @@ private: varp->isStatic(true); varp->valuep(constp); m_modp->addStmtp(varp); - m_hashed.hashAndInsert(constp); + m_dupFinder.insert(constp); nodep->user2p(varp); ++m_staticConstantsExtracted; } else { @@ -230,12 +229,12 @@ private: virtual void visit(AstNodeModule* nodep) override { UINFO(4, " MOD " << nodep << endl); UASSERT_OBJ(m_modp == nullptr, nodep, "Nested modules ?"); - UASSERT_OBJ(m_hashed.mmap().empty(), nodep, "Statements outside module ?"); + UASSERT_OBJ(m_dupFinder.empty(), nodep, "Statements outside module ?"); m_modp = nodep; m_cfuncp = nullptr; iterateChildren(nodep); m_modp = nullptr; - m_hashed.clear(); + m_dupFinder.clear(); } virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_cfuncp); diff --git a/src/V3ProtectLib.cpp b/src/V3ProtectLib.cpp index ec267187c..7c42f868c 100644 --- a/src/V3ProtectLib.cpp +++ b/src/V3ProtectLib.cpp @@ -20,7 +20,7 @@ #include "V3Global.h" #include "V3String.h" #include "V3ProtectLib.h" -#include "V3Hashed.h" +#include "V3Hasher.h" #include "V3Task.h" #include @@ -87,7 +87,7 @@ private: iterateChildren(nodep); - V3Hash hash = V3Hashed::uncachedHash(m_cfilep); + V3Hash hash = V3Hasher::uncachedHash(m_cfilep); m_hashValuep->addText(fl, cvtToStr(hash.fullValue()) + ";\n"); m_cHashValuep->addText(fl, cvtToStr(hash.fullValue()) + "U;\n"); m_foundTop = true; diff --git a/src/V3SenTree.h b/src/V3SenTree.h index 501157652..78d5f3a34 100644 --- a/src/V3SenTree.h +++ b/src/V3SenTree.h @@ -23,7 +23,7 @@ #include "verilatedos.h" #include "V3Ast.h" -#include "V3Hashed.h" +#include "V3Hasher.h" #include @@ -37,7 +37,7 @@ private: // TYPES struct HashSenTree { size_t operator()(const AstSenTree* kp) const { - return V3Hashed::uncachedHash(kp).fullValue(); + return V3Hasher::uncachedHash(kp).fullValue(); } }; diff --git a/src/V3Table.cpp b/src/V3Table.cpp index c31adb692..eb29e3639 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -342,7 +342,7 @@ private: AstVarScope* findDuplicateTable(AstVarScope* vsc1p) { // See if another table we've created is identical, if so use it for both. - // (A more 'modern' way would be to instead use V3Hashed::findDuplicate) + // (A more 'modern' way would be to instead use V3DupFinder::findDuplicate) AstVar* var1p = vsc1p->varp(); for (AstVarScope* vsc2p : m_modTableVscs) { AstVar* var2p = vsc2p->varp(); diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 49ee14933..601c285e0 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -42,7 +42,7 @@ #include "V3Trace.h" #include "V3EmitCBase.h" #include "V3Graph.h" -#include "V3Hashed.h" +#include "V3DupFinder.h" #include "V3Stats.h" #include @@ -154,8 +154,8 @@ public: class TraceVisitor final : public EmitCBaseVisitor { private: // NODE STATE - // V3Hashed - // Ast*::user4() // V3Hashed calculation + // V3Hasher in V3DupFinder + // Ast*::user4() // V3Hasher calculation // Cleared entire netlist // AstCFunc::user1() // V3GraphVertex* for this node // AstTraceDecl::user1() // V3GraphVertex* for this node @@ -165,7 +165,7 @@ private: AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; AstUser3InUse m_inuser3; - // AstUser4InUse In V3Hashed + // AstUser4InUse In V3Hasher via V3DupFinder // STATE AstNodeModule* m_topModp = nullptr; // Module to add variables to @@ -194,7 +194,7 @@ private: void detectDuplicates() { UINFO(9, "Finding duplicates\n"); // Note uses user4 - V3Hashed hashed; // Duplicate code detection + V3DupFinder dupFinder; // Duplicate code detection // Hash all of the values the traceIncs need for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { @@ -205,13 +205,9 @@ private: UASSERT_OBJ(nodep->valuep()->backp() == nodep, nodep, "Trace duplicate back needs consistency," " so we can map duplicates back to TRACEINCs"); - hashed.hash(nodep->valuep()); - UINFO(8, " Hashed " << std::hex << hashed.nodeHash(nodep->valuep()) << " " - << nodep << endl); - // Just keep one node in the map and point all duplicates to this node - if (hashed.findDuplicate(nodep->valuep()) == hashed.end()) { - hashed.hashAndInsert(nodep->valuep()); + if (dupFinder.findDuplicate(nodep->valuep()) == dupFinder.end()) { + dupFinder.insert(nodep->valuep()); } } } @@ -221,10 +217,10 @@ private: if (TraceTraceVertex* const vvertexp = dynamic_cast(itp)) { AstTraceDecl* const nodep = vvertexp->nodep(); if (nodep->valuep() && !vvertexp->duplicatep()) { - const auto dupit = hashed.findDuplicate(nodep->valuep()); - if (dupit != hashed.end()) { + const auto dupit = dupFinder.findDuplicate(nodep->valuep()); + if (dupit != dupFinder.end()) { const AstTraceDecl* const dupDeclp - = VN_CAST_CONST(hashed.iteratorNodep(dupit)->backp(), TraceDecl); + = VN_CAST_CONST(dupit->second->backp(), TraceDecl); UASSERT_OBJ(dupDeclp, nodep, "Trace duplicate of wrong type"); TraceTraceVertex* const dupvertexp = dynamic_cast(dupDeclp->user1u().toGraphVertex()); @@ -237,7 +233,6 @@ private: } } } - hashed.clear(); } void graphSimplify(bool initial) {