Skip to content

Commit

Permalink
Split V3Hashed to V3Hasher and V3DupFinder (verilator#2967)
Browse files Browse the repository at this point in the history
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 (verilator#2964)
  • Loading branch information
gezalore committed May 21, 2021
1 parent a44d2b2 commit fd35492
Show file tree
Hide file tree
Showing 17 changed files with 389 additions and 382 deletions.
3 changes: 2 additions & 1 deletion src/Makefile_obj.in
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ RAW_OBJS = \
V3Depth.o \
V3DepthBlock.o \
V3Descope.o \
V3DupFinder.o \
V3EmitC.o \
V3EmitCInlines.o \
V3EmitCSyms.o \
Expand All @@ -202,7 +203,7 @@ RAW_OBJS = \
V3GraphDfa.o \
V3GraphPathChecker.o \
V3GraphTest.o \
V3Hashed.o \
V3Hasher.o \
V3HierBlock.o \
V3Inline.o \
V3Inst.o \
Expand Down
9 changes: 3 additions & 6 deletions src/V3Ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -1358,12 +1358,9 @@ class V3Hash final {
}
// 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());
Expand Down
18 changes: 9 additions & 9 deletions src/V3Combine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

#include "V3Global.h"
#include "V3Combine.h"
#include "V3Hashed.h"
#include "V3DupFinder.h"
#include "V3Stats.h"
#include "V3Ast.h"

Expand Down Expand Up @@ -114,16 +114,16 @@ class CombineVisitor final : CombBaseVisitor {
// 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;
Expand All @@ -144,14 +144,14 @@ class CombineVisitor final : CombBaseVisitor {
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,
Expand Down Expand Up @@ -184,10 +184,10 @@ class CombineVisitor final : CombBaseVisitor {
}
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
Expand All @@ -196,7 +196,7 @@ class CombineVisitor final : CombBaseVisitor {
virtual void visit(AstCFunc* nodep) override {
if (nodep->dontCombine()) return;
// Hash the entire function
m_hashed.hashAndInsert(nodep);
m_dupFinder.insert(nodep);
}

//--------------------
Expand Down
19 changes: 8 additions & 11 deletions src/V3CoverageJoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

#include "V3Global.h"
#include "V3CoverageJoin.h"
#include "V3Hashed.h"
#include "V3DupFinder.h"
#include "V3Stats.h"

#include <vector>
Expand All @@ -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
Expand All @@ -49,9 +46,9 @@ class CoverageJoinVisitor final : public AstNVisitor {
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.
Expand All @@ -60,10 +57,10 @@ class CoverageJoinVisitor final : public AstNVisitor {
// 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:
Expand All @@ -82,7 +79,7 @@ class CoverageJoinVisitor final : public AstNVisitor {
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;
}
}
Expand Down
97 changes: 97 additions & 0 deletions src/V3DupFinder.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 82 additions & 0 deletions src/V3DupFinder.h
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit fd35492

Please sign in to comment.