verilator/src/V3Hasher.cpp

542 lines
20 KiB
C++
Raw Normal View History

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: AstNode hash computation
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
2023-01-01 16:18:39 +01:00
// Copyright 2003-2023 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"
#include <functional>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Visitor that computes node hashes
class HasherVisitor final : public VNVisitor {
private:
// NODE STATE
// AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
// VNUser4InUse in V3Hasher.h
// STATE
V3Hash m_hash; // Hash value accumulator
const bool m_cacheInUser4; // Use user4 to cache each V3Hash?
// METHODS
V3Hash hashNodeAndIterate(AstNode* nodep, bool hashDType, bool hashChildren,
std::function<void()>&& f) {
// See comments in visit(AstCFunc) about this breaking recursion
if (m_cacheInUser4 && nodep->user4()) {
2022-11-20 19:11:01 +01:00
return V3Hash{nodep->user4()};
} else {
VL_RESTORER(m_hash);
// Reset accumulator
2022-11-20 19:11:01 +01:00
m_hash = V3Hash{nodep->type()}; // Node type
f(); // Node specific hash
if (hashDType && nodep != nodep->dtypep()) iterateNull(nodep->dtypep()); // Node dtype
if (hashChildren) iterateChildrenConst(nodep); // Children
if (m_cacheInUser4) nodep->user4(m_hash.value());
return m_hash;
}
}
// VISITORS
constexpr static bool HASH_DTYPE = true;
constexpr static bool HASH_CHILDREN = true;
// Each visitor below contributes to the hash any node specific content
// that is not dependent on either of the following, as these are
// included by default by hashNode:
// - Node type (as given by AstNode::type())
// - Node dtype (unless !hashDType)
// - child nodes (unless !hashChildren)
//
// The hash must be stable, which means in particular it cannot rely on
// pointer values, or any other value that might differ between separate
// invocations of Verilator over the same design.
//
// Note there is a circularity problem where some child nodes can back
// to their ancestral nodes via member pointers, which can lead to an
// infinite traversal. To break this, nodes that are subject to such
// referencing and represent code which can reasonably be assumed not to
// be equivalent to any other code, are hashed either by name (e.g.:
// AstNodeModule), or by unique identifier (e.g.: AstNodeUOrStructDType).
//------------------------------------------------------------
// AstNode - Warns to help find missing cases
void visit(AstNode* nodep) override {
#if VL_DEBUG
UINFO(0, "%Warning: Hashing node as AstNode: " << nodep << endl);
#endif
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
//------------------------------------------------------------
// AstNodeDType
void visit(AstNodeArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
iterateNull(nodep->virtRefDTypep());
m_hash += nodep->left();
m_hash += nodep->right();
});
}
void visit(AstNodeUOrStructDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, false, [=]() { //
m_hash += nodep->uniqueNum();
});
}
void visit(AstParamTypeDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
m_hash += nodep->varType();
});
}
void visit(AstMemberDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
void visit(AstDefImplicitDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->uniqueNum();
});
}
void visit(AstAssocArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
iterateNull(nodep->virtRefDTypep());
iterateNull(nodep->virtRefDType2p());
});
}
void visit(AstDynArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->virtRefDTypep());
});
}
void visit(AstUnsizedArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->virtRefDTypep());
});
}
void visit(AstWildcardArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->virtRefDTypep());
});
}
void visit(AstBasicDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
m_hash += nodep->keyword();
m_hash += nodep->nrange().left();
m_hash += nodep->nrange().right();
});
}
void visit(AstConstDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateNull(nodep->virtRefDTypep());
});
}
void visit(AstClassRefDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->classp());
});
}
void visit(AstIfaceRefDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->cellp());
});
}
void visit(AstQueueDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->virtRefDTypep());
});
}
void visit(AstRefDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
iterateNull(nodep->typedefp());
iterateNull(nodep->refDTypep());
});
}
void visit(AstVoidDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
}
void visit(AstEnumDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, false, [=]() { //
m_hash += nodep->uniqueNum();
});
}
//------------------------------------------------------------
// AstNodeExpr
void visit(AstNodeExpr* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstConst* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->num().toHash();
});
}
void visit(AstNullCheck* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstCCast* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->size();
});
}
void visit(AstVarRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
if (nodep->varScopep()) {
iterateNull(nodep->varScopep());
} else {
iterateNull(nodep->varp());
m_hash += nodep->selfPointer();
}
});
}
void visit(AstVarXRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
iterateNull(nodep->varp());
m_hash += nodep->dotted();
});
}
void visit(AstMemberSel* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
void visit(AstFScanF* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->text();
});
}
void visit(AstSScanF* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->text();
});
}
void visit(AstAddrOfCFunc* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateNull(nodep->funcp());
});
}
//------------------------------------------------------------
// AstNodeStmt
void visit(AstNodeStmt* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
}
void visit(AstNodeText* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->text();
});
}
void visit(AstNodeCCall* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->funcp());
});
}
void visit(AstNodeFTaskRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
iterateNull(nodep->taskp());
iterateNull(nodep->classOrPackagep());
});
}
void visit(AstCMethodHard* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
2022-09-16 17:15:10 +02:00
void visit(AstCAwait* nodep) override {
Timing support (#3363) Adds timing support to Verilator. It makes it possible to use delays, event controls within processes (not just at the start), wait statements, and forks. Building a design with those constructs requires a compiler that supports C++20 coroutines (GCC 10, Clang 5). The basic idea is to have processes and tasks with delays/event controls implemented as C++20 coroutines. This allows us to suspend and resume them at any time. There are five main runtime classes responsible for managing suspended coroutines: * `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle` with move semantics and automatic cleanup. * `VlDelayScheduler`, for coroutines suspended by delays. It resumes them at a proper simulation time. * `VlTriggerScheduler`, for coroutines suspended by event controls. It resumes them if its corresponding trigger was set. * `VlForkSync`, used for syncing `fork..join` and `fork..join_any` blocks. * `VlCoroutine`, the return type of all verilated coroutines. It allows for suspending a stack of coroutines (normally, C++ coroutines are stackless). There is a new visitor in `V3Timing.cpp` which: * scales delays according to the timescale, * simplifies intra-assignment timing controls and net delays into regular timing controls and assignments, * simplifies wait statements into loops with event controls, * marks processes and tasks with timing controls in them as suspendable, * creates delay, trigger scheduler, and fork sync variables, * transforms timing controls and fork joins into C++ awaits There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`) that integrate static scheduling with timing. This involves providing external domains for variables, so that the necessary combinational logic gets triggered after coroutine resumption, as well as statements that need to be injected into the design eval function to perform this resumption at the correct time. There is also a function that transforms forked processes into separate functions. See the comments in `verilated_timing.h`, `verilated_timing.cpp`, `V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals documentation for more details. Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateNull(nodep->sensesp());
});
}
void visit(AstCoverInc* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->declp());
});
}
void visit(AstDisplay* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->displayType();
});
}
void visit(AstMonitorOff* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->off();
});
}
void visit(AstJumpGo* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->labelp());
});
}
void visit(AstTraceInc* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->declp());
});
}
void visit(AstNodeCoverOrAssert* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
//------------------------------------------------------------
2022-12-23 17:32:38 +01:00
// AstNode direct descendants
void visit(AstNodeRange* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstNodeModule* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() { //
m_hash += nodep->origName();
});
}
void visit(AstNodePreSel* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstClassExtends* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstSelLoopVars* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstDefParam* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstArg* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstParseRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->expect();
m_hash += nodep->name();
});
}
void visit(AstClassOrPackageRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateNull(nodep->classOrPackageNodep());
});
}
void visit(AstSenItem* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->edgeType();
});
}
void visit(AstSenTree* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstSFormatF* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->text();
});
}
void visit(AstElabDisplay* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->displayType();
});
}
void visit(AstInitItem* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstInitArray* nodep) override {
if (const AstAssocArrayDType* const dtypep = VN_CAST(nodep->dtypep(), AssocArrayDType)) {
if (nodep->defaultp()) {
m_hash
+= hashNodeAndIterate(nodep->defaultp(), HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
const auto& mapr = nodep->map();
for (const auto& itr : mapr) { // mapr is sorted, so hash should get stable results
m_hash += itr.first;
m_hash += hashNodeAndIterate(itr.second, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
} else if (const AstUnpackArrayDType* const dtypep
= VN_CAST(nodep->dtypep(), UnpackArrayDType)) {
// Hash unpacked array initializers by value, as the order of initializer nodes does
// not matter, and we want semantically equivalent initializers to map to the same
// hash.
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, /* hashChildren: */ !dtypep, [=]() {
if (dtypep) {
const uint32_t size = dtypep->elementsConst();
for (uint32_t n = 0; n < size; ++n) { //
iterateNull(nodep->getIndexDefaultedValuep(n));
}
}
});
}
}
void visit(AstPragma* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->pragType();
});
}
void visit(AstAttrOf* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->attrType();
});
}
void visit(AstNodeFile* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
void visit(AstCFunc* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
// We might be in a recursive function, if so on *second* call
// here we need to break what would be an infinite loop.
2022-11-20 19:11:01 +01:00
nodep->user4(V3Hash{1}.value()); // Set this "first" call
// So that a second call will then exit hashNodeAndIterate
// Having a constant in the hash just means the recursion will
// end, it shouldn't change the CFunc having a unique hash itself.
m_hash += nodep->isLoose();
});
}
void visit(AstVar* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
m_hash += nodep->varType();
});
}
void visit(AstScope* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() {
m_hash += nodep->name();
iterateNull(nodep->aboveScopep());
});
}
void visit(AstVarScope* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
iterateNull(nodep->varp());
iterateNull(nodep->scopep());
});
}
void visit(AstEnumItem* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
void visit(AstTypedef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
void visit(AstTypedefFwd* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
void visit(AstActive* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateNull(nodep->sensesp());
});
}
void visit(AstCell* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
iterateNull(nodep->modp());
});
}
void visit(AstCellInline* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
iterateNull(nodep->scopep());
});
}
void visit(AstNodeFTask* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
void visit(AstModport* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
void visit(AstModportVarRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
iterateNull(nodep->varp());
});
}
void visit(AstModportFTaskRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
iterateNull(nodep->ftaskp());
});
}
void visit(AstMTaskBody* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstNodeProcedure* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstNodeBlock* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
void visit(AstPin* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
m_hash += nodep->pinNum();
});
}
public:
// CONSTRUCTORS
explicit HasherVisitor(AstNode* nodep)
: m_cacheInUser4{true} {
iterate(nodep);
}
class Uncached {};
HasherVisitor(const AstNode* nodep, Uncached)
: m_cacheInUser4{false} {
iterate(const_cast<AstNode*>(nodep));
}
V3Hash finalHash() const { return m_hash; }
~HasherVisitor() override = default;
};
//######################################################################
// V3Hasher methods
V3Hash V3Hasher::operator()(AstNode* nodep) const {
if (!nodep->user4()) HasherVisitor{nodep};
2022-11-20 19:11:01 +01:00
return V3Hash{nodep->user4()};
}
V3Hash V3Hasher::rehash(AstNode* nodep) const {
nodep->user4(0);
{ HasherVisitor{nodep}; }
2022-11-20 19:11:01 +01:00
return V3Hash{nodep->user4()};
}
V3Hash V3Hasher::uncachedHash(const AstNode* nodep) {
const HasherVisitor visitor{nodep, HasherVisitor::Uncached{}};
return visitor.finalHash();
}
//######################################################################
// This is used by the std::hash specialization for VNRef.
// Declared separately to avoid a circular header dependency.
size_t V3HasherUncachedHash(const AstNode& node) {
return static_cast<size_t>(V3Hasher::uncachedHash(&node).value());
}