Split V3Hashed to V3Hasher and V3DupFinder (#2967)

V3Hasher is responsible for computing AstNode hashes, while V3DupFinder
can be used to find duplicate trees based on hashes. Interface of
V3DupFinder simplified somewhat. No functional change intended at this
point, but hash computation might differ in minor details, this however
should have no perceivable effect on output/runtime.

Implements (#2964)
This commit is contained in:
Geza Lore 2021-05-21 01:41:46 +01:00 committed by GitHub
parent a44d2b2570
commit fd35492226
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 389 additions and 382 deletions

View File

@ -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 \

View File

@ -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());

View File

@ -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);
}
//--------------------

View File

@ -22,7 +22,7 @@
#include "V3Global.h"
#include "V3CoverageJoin.h"
#include "V3Hashed.h"
#include "V3DupFinder.h"
#include "V3Stats.h"
#include <vector>
@ -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<AstCoverToggle*> 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;
}
}

97
src/V3DupFinder.cpp Normal file
View File

@ -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 <algorithm>
#include <iomanip>
#include <map>
#include <memory>
//######################################################################
// 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<std::ofstream> logp(V3File::new_ofstream(filename));
if (logp->fail()) v3fatal("Can't write " << filename);
std::unordered_map<int, int> 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);
}
}

82
src/V3DupFinder.h Normal file
View File

@ -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 <map>
//============================================================================
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<V3Hash, AstNode*> {
using Super = std::multimap<V3Hash, AstNode*>;
// 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

View File

@ -30,7 +30,7 @@
#include "V3Graph.h"
#include "V3Const.h"
#include "V3Stats.h"
#include "V3Hashed.h"
#include "V3DupFinder.h"
#include <algorithm>
#include <list>
@ -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<AstNode*> 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");
}
}
}

View File

@ -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 <algorithm>
#include <iomanip>
#include <map>
#include <memory>
//######################################################################
// 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 "<<m_lowerHash<<" "<<nodep<<endl);
thisHash = m_lowerHash;
}
}
// Update what will become the above node's hash
m_lowerHash += m_cacheInUser4 ? V3Hashed::nodeHash(nodep) : thisHash;
}
//--------------------
virtual void visit(AstVar*) override {}
virtual void visit(AstTypedef*) override {}
virtual void visit(AstParamTypeDType*) override {}
virtual void visit(AstNode* nodep) override {
// Hash not just iterate
nodeHashIterate(nodep);
}
public:
// CONSTRUCTORS
explicit HashedVisitor(AstNode* nodep)
: m_cacheInUser4{true} {
nodeHashIterate(nodep);
// UINFO(9," stmthash "<<hex<<V3Hashed::nodeHash(nodep)<<" "<<nodep<<endl);
}
explicit HashedVisitor(const AstNode* nodep)
: m_cacheInUser4{false} {
nodeHashIterate(const_cast<AstNode*>(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<std::ofstream> logp(V3File::new_ofstream(filename));
if (logp->fail()) v3fatal("Can't write " << filename);
std::unordered_map<int, int> 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<HashMmap::iterator, HashMmap::iterator> 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();
}

View File

@ -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<V3Hash, AstNode*>;
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

91
src/V3Hasher.cpp Normal file
View File

@ -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<AstNode*>(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();
}

51
src/V3Hasher.h Normal file
View File

@ -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

View File

@ -56,7 +56,7 @@
#include "V3Parse.h"
#include "V3Width.h"
#include "V3Unroll.h"
#include "V3Hashed.h"
#include "V3Hasher.h"
#include <deque>
#include <map>
@ -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;

View File

@ -30,7 +30,7 @@
#include "V3Global.h"
#include "V3Premit.h"
#include "V3Ast.h"
#include "V3Hashed.h"
#include "V3DupFinder.h"
#include "V3Stats.h"
#include <algorithm>
@ -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);

View File

@ -20,7 +20,7 @@
#include "V3Global.h"
#include "V3String.h"
#include "V3ProtectLib.h"
#include "V3Hashed.h"
#include "V3Hasher.h"
#include "V3Task.h"
#include <list>
@ -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;

View File

@ -23,7 +23,7 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Hashed.h"
#include "V3Hasher.h"
#include <unordered_set>
@ -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();
}
};

View File

@ -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();

View File

@ -42,7 +42,7 @@
#include "V3Trace.h"
#include "V3EmitCBase.h"
#include "V3Graph.h"
#include "V3Hashed.h"
#include "V3DupFinder.h"
#include "V3Stats.h"
#include <map>
@ -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<TraceTraceVertex*>(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<TraceTraceVertex*>(dupDeclp->user1u().toGraphVertex());
@ -237,7 +233,6 @@ private:
}
}
}
hashed.clear();
}
void graphSimplify(bool initial) {