verilator/src/V3Hasher.cpp

605 lines
24 KiB
C++
Raw Normal View History

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: AstNode hash computation
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
2025-01-01 14:30:25 +01:00
// Copyright 2003-2025 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
//
//*************************************************************************
2023-10-18 04:50:27 +02:00
#include "V3PchAstMT.h"
#include "V3Hasher.h"
#include <functional>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Visitor that computes node hashes
class HasherVisitor final : public VNVisitorConst {
// 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?
std::set<AstNode*> m_visited; // Keeps track of some visited nodes to prevent
// infinite recursion
// METHODS
void guardRecursion(AstNode* const nodep, std::function<void()>&& f) {
// Guard against infinite recursion if there's no caching
// Otherwise caching does the same but faster
if (!m_cacheInUser4) {
if (m_visited.find(nodep) != m_visited.end()) {
m_hash += V3Hash{nodep->name()};
return;
}
m_visited.insert(nodep);
}
f();
}
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
2024-09-11 01:04:55 +02:00
f(); // Node-specific hash
if (hashDType && nodep != nodep->dtypep())
iterateConstNull(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;
2024-09-11 01:04:55 +02:00
// 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);
#endif
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
//------------------------------------------------------------
// AstNodeDType
void visit(AstNodeArrayDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() {
iterateConstNull(nodep->virtRefDTypep());
m_hash += nodep->left();
m_hash += nodep->right();
});
}
void visit(AstNodeUOrStructDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, false, [this, nodep]() { //
m_hash += nodep->uniqueNum();
});
}
void visit(AstParamTypeDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() {
m_hash += nodep->name();
m_hash += nodep->varType();
});
}
void visit(AstMemberDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
});
}
void visit(AstDefImplicitDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->uniqueNum();
});
}
void visit(AstAssocArrayDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() {
iterateConstNull(nodep->virtRefDTypep());
iterateConstNull(nodep->virtRefDType2p());
});
}
void visit(AstBracketArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() {
iterateConstNull(nodep->virtRefDTypep());
});
}
void visit(AstDynArrayDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->virtRefDTypep());
});
}
void visit(AstUnsizedArrayDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->virtRefDTypep());
});
}
void visit(AstWildcardArrayDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->virtRefDTypep());
});
}
void visit(AstBasicDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() {
m_hash += nodep->keyword();
m_hash += nodep->numeric();
m_hash += nodep->nrange().left();
m_hash += nodep->nrange().right();
});
}
2023-09-19 03:17:21 +02:00
void visit(AstCDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
2023-09-19 03:17:21 +02:00
m_hash += nodep->name();
});
}
void visit(AstConstDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->virtRefDTypep());
});
}
void visit(AstClassRefDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->classp());
});
}
void visit(AstIfaceRefDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->cellp());
});
}
void visit(AstQueueDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->virtRefDTypep());
});
}
void visit(AstRefDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() {
m_hash += nodep->name();
iterateConstNull(nodep->typedefp());
iterateConstNull(nodep->refDTypep());
});
}
void visit(AstStreamDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, []() {});
}
void visit(AstVoidDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, []() {});
}
void visit(AstEnumDType* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, false, [this, nodep]() { //
m_hash += nodep->uniqueNum();
});
}
//------------------------------------------------------------
// AstNodeExpr
void visit(AstNodeExpr* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstSel* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->widthConst();
});
}
void visit(AstConst* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->num().toHash();
});
}
void visit(AstNullCheck* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstCCast* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->size();
});
}
void visit(AstVarRef* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() {
if (nodep->varScopep()) {
iterateConstNull(nodep->varScopep());
} else {
iterateConstNull(nodep->varp());
m_hash += nodep->selfPointer().asString();
}
});
}
void visit(AstVarXRef* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() {
iterateConstNull(nodep->varp());
m_hash += nodep->dotted();
});
}
void visit(AstMemberSel* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
});
}
void visit(AstFScanF* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->text();
});
}
void visit(AstSScanF* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->text();
});
}
void visit(AstAddrOfCFunc* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->funcp());
});
}
//------------------------------------------------------------
// AstNodeStmt
void visit(AstNodeStmt* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, []() {});
}
void visit(AstNodeCCall* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->funcp());
});
}
void visit(AstNodeFTaskRef* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() {
iterateConstNull(nodep->taskp());
iterateConstNull(nodep->classOrPackagep());
});
}
void visit(AstCMethodHard* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->method();
});
}
2022-09-16 17:15:10 +02:00
void visit(AstCAwait* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->sentreep());
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
});
}
void visit(AstCLocalScope* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstCoverInc* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->declp());
});
}
void visit(AstDisplay* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->displayType();
});
}
void visit(AstMonitorOff* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->off();
});
}
void visit(AstJumpGo* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, false, []() {});
}
void visit(AstTraceInc* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->declp());
});
}
void visit(AstNodeCoverOrAssert* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
});
}
//------------------------------------------------------------
2022-12-23 17:32:38 +01:00
// AstNode direct descendants
void visit(AstNodeRange* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstNodeModule* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [this, nodep]() { //
m_hash += nodep->name();
});
}
void visit(AstNodePreSel* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstClassExtends* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstSelLoopVars* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstDefParam* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstArg* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstParseRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
});
}
void visit(AstClassOrPackageRef* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->classOrPackageNodep());
});
}
void visit(AstSenItem* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->edgeType();
});
}
void visit(AstSenTree* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstSFormatF* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->text();
});
}
void visit(AstElabDisplay* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->displayType();
});
}
void visit(AstInitItem* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstInitArray* nodep) override {
if (VN_IS(nodep->dtypep(), AssocArrayDType)) {
if (nodep->defaultp()) {
m_hash
2023-11-06 13:13:31 +01:00
+= 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;
2023-11-06 13:13:31 +01:00
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.
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(
nodep, HASH_DTYPE, /* hashChildren: */ !dtypep, [this, nodep, dtypep]() {
if (dtypep) {
const uint32_t size = dtypep->elementsConst();
for (uint32_t n = 0; n < size; ++n) { //
iterateConstNull(nodep->getIndexDefaultedValuep(n));
}
}
2023-11-06 13:13:31 +01:00
});
}
}
void visit(AstPragma* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->pragType();
});
}
void visit(AstAttrOf* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->attrType();
});
}
void visit(AstNodeFile* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
});
}
void visit(AstCFunc* nodep) override {
guardRecursion(nodep, [this, nodep]() { //
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
// We might be in a recursive function, if so on *second* call
// here we need to break what would be an infinite loop.
if (m_cacheInUser4) 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 {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() {
m_hash += nodep->name();
m_hash += nodep->varType();
});
}
void visit(AstScope* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [this, nodep]() {
m_hash += nodep->name();
iterateConstNull(nodep->aboveScopep());
});
}
void visit(AstVarScope* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() {
iterateConstNull(nodep->varp());
iterateConstNull(nodep->scopep());
});
}
void visit(AstEnumItem* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
});
}
void visit(AstTypedef* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
});
}
void visit(AstTypedefFwd* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
});
}
void visit(AstActive* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
iterateConstNull(nodep->sentreep());
});
}
void visit(AstCell* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() {
m_hash += nodep->name();
iterateConstNull(nodep->modp());
});
}
void visit(AstCellInline* nodep) override {
2024-04-02 05:11:15 +02:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN,
[this, nodep]() { m_hash += nodep->name(); });
}
void visit(AstNodeFTask* nodep) override {
guardRecursion(nodep, [this, nodep]() { //
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
// See comments in AstCFunc
if (m_cacheInUser4) nodep->user4(V3Hash{1}.value());
});
});
}
void visit(AstModport* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
});
}
void visit(AstModportVarRef* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() {
m_hash += nodep->name();
iterateConstNull(nodep->varp());
});
}
void visit(AstModportFTaskRef* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() {
m_hash += nodep->name();
iterateConstNull(nodep->ftaskp());
});
}
void visit(AstMTaskBody* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstNodeProcedure* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstBegin* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
});
}
void visit(AstFork* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->name();
m_hash += nodep->joinType();
});
}
void visit(AstPin* nodep) override {
2023-11-06 13:13:31 +01:00
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [this, nodep]() {
m_hash += nodep->name();
m_hash += nodep->pinNum();
});
}
Internals: Refactor text based Ast constructs (#6280) (#6571) Remove the large variety of ways raw "text" is represented in the Ast. Particularly, the only thing that represents a string to be emitted in the output is AstText. There are 5 AstNodes that can contain AstText, and V3Emit will throw an error if an AstText is encountered anywhere else: - AstCStmt: Internally generated procedural statements involving raw text. - AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to AstCStmt, as it's largely equivalent. We should never create this internally unless used to represent user input. It is used for $c, statements in the input, and for some 'systemc_* blocks. - AstCExpr: Internally generaged expression involving raw text. - AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to AstCExpr. It is largely equivalent, but also has more optimizations disabled. This should never be created internally, it is only used for $c expressions in the input. - AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical wrappers. Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and AstTextBlock, as these are always generated by us, and should always be well formed. Tracking is always off for AstCStmtUser and AstCExprUser, as these contain arbitrary user input that might not be safe to parse for indentation. Remove subsequently redundant AstNodeSimpleText and AstNodeText types. This patch also fixes incorrect indentation in emitted waveform tracing functions, and makes the output more readable for hier block SV stubs. With that, all raw text nodes are handled as a proper AstNodeStmt or AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
void visit(AstText* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->text();
});
}
void visit(AstTextBlock* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
m_hash += nodep->prefix();
m_hash += nodep->separator();
m_hash += nodep->suffix();
});
}
void visit(AstCStmt* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, []() {});
}
void visit(AstCStmtUser* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, []() {});
}
void visit(AstCExpr* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstCExprUser* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
public:
// CONSTRUCTORS
explicit HasherVisitor(AstNode* nodep)
: m_cacheInUser4{true} {
iterateConst(nodep);
}
class Uncached {};
HasherVisitor(const AstNode* nodep, Uncached)
: m_cacheInUser4{false} {
iterateConst(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());
}