2021-05-21 02:41:46 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
2021-05-21 15:34:27 +02:00
|
|
|
// DESCRIPTION: Verilator: AstNode hash computation
|
2021-05-21 02:41:46 +02:00
|
|
|
//
|
|
|
|
|
// 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
|
2021-05-21 02:41:46 +02:00
|
|
|
// 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"
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3Hasher.h"
|
|
|
|
|
|
2021-05-21 15:34:27 +02:00
|
|
|
#include <functional>
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2021-05-21 02:41:46 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Visitor that computes node hashes
|
|
|
|
|
|
2023-03-18 17:17:25 +01:00
|
|
|
class HasherVisitor final : public VNVisitorConst {
|
2021-05-21 02:41:46 +02:00
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
|
2022-01-02 19:56:40 +01:00
|
|
|
// VNUser4InUse in V3Hasher.h
|
2021-05-21 02:41:46 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2021-05-21 15:34:27 +02:00
|
|
|
V3Hash m_hash; // Hash value accumulator
|
2021-05-21 02:41:46 +02:00
|
|
|
const bool m_cacheInUser4; // Use user4 to cache each V3Hash?
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
|
2021-05-21 15:34:27 +02:00
|
|
|
V3Hash hashNodeAndIterate(AstNode* nodep, bool hashDType, bool hashChildren,
|
|
|
|
|
std::function<void()>&& f) {
|
2022-01-05 02:19:58 +01:00
|
|
|
// See comments in visit(AstCFunc) about this breaking recursion
|
2021-05-21 15:34:27 +02:00
|
|
|
if (m_cacheInUser4 && nodep->user4()) {
|
2022-11-20 19:11:01 +01:00
|
|
|
return V3Hash{nodep->user4()};
|
2021-05-21 15:34:27 +02:00
|
|
|
} else {
|
|
|
|
|
VL_RESTORER(m_hash);
|
|
|
|
|
// Reset accumulator
|
2022-11-20 19:11:01 +01:00
|
|
|
m_hash = V3Hash{nodep->type()}; // Node type
|
2021-05-21 15:34:27 +02:00
|
|
|
f(); // Node specific hash
|
2023-03-18 17:17:25 +01:00
|
|
|
if (hashDType && nodep != nodep->dtypep())
|
|
|
|
|
iterateConstNull(nodep->dtypep()); // Node dtype
|
2021-05-21 15:34:27 +02:00
|
|
|
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
|
2021-05-21 02:41:46 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
#if VL_DEBUG
|
2021-09-25 03:11:15 +02:00
|
|
|
UINFO(0, "%Warning: Hashing node as AstNode: " << nodep << endl);
|
2021-05-21 15:34:27 +02:00
|
|
|
#endif
|
|
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------
|
|
|
|
|
// AstNodeDType
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeArrayDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->virtRefDTypep());
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += nodep->left();
|
|
|
|
|
m_hash += nodep->right();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeUOrStructDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, false, [=]() { //
|
|
|
|
|
m_hash += nodep->uniqueNum();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstParamTypeDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
m_hash += nodep->varType();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstMemberDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDefImplicitDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->uniqueNum();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssocArrayDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->virtRefDTypep());
|
|
|
|
|
iterateConstNull(nodep->virtRefDType2p());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDynArrayDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->virtRefDTypep());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstUnsizedArrayDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->virtRefDTypep());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstWildcardArrayDType* nodep) override {
|
2022-07-20 15:01:36 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->virtRefDTypep());
|
2022-07-20 15:01:36 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstBasicDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
|
|
|
|
m_hash += nodep->keyword();
|
2023-10-15 21:15:46 +02:00
|
|
|
m_hash += nodep->numeric();
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += nodep->nrange().left();
|
|
|
|
|
m_hash += nodep->nrange().right();
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-09-19 03:17:21 +02:00
|
|
|
void visit(AstCDType* nodep) override {
|
|
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConstDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->virtRefDTypep());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClassRefDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->classp());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstIfaceRefDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->cellp());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstQueueDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->virtRefDTypep());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstRefDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
|
|
|
|
m_hash += nodep->name();
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->typedefp());
|
|
|
|
|
iterateConstNull(nodep->refDTypep());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2023-09-16 00:33:02 +02:00
|
|
|
void visit(AstStreamDType* nodep) override {
|
|
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVoidDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstEnumDType* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, false, [=]() { //
|
|
|
|
|
m_hash += nodep->uniqueNum();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------
|
2022-10-12 11:19:21 +02:00
|
|
|
// AstNodeExpr
|
|
|
|
|
void visit(AstNodeExpr* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConst* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->num().toHash();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNullCheck* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCCast* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->size();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
|
|
|
|
if (nodep->varScopep()) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->varScopep());
|
2021-05-21 15:34:27 +02:00
|
|
|
} else {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->varp());
|
2023-09-08 13:34:35 +02:00
|
|
|
m_hash += nodep->selfPointer().asString();
|
2021-05-21 02:41:46 +02:00
|
|
|
}
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarXRef* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->varp());
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += nodep->dotted();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstMemberSel* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFScanF* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->text();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSScanF* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->text();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAddrOfCFunc* nodep) override {
|
2021-06-13 15:33:11 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->funcp());
|
2021-06-13 15:33:11 +02:00
|
|
|
});
|
|
|
|
|
}
|
2021-05-21 15:34:27 +02:00
|
|
|
|
|
|
|
|
//------------------------------------------------------------
|
|
|
|
|
// AstNodeStmt
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeStmt* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeText* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->text();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeCCall* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->funcp());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->taskp());
|
|
|
|
|
iterateConstNull(nodep->classOrPackagep());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCMethodHard* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
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, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->sensesp());
|
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
|
|
|
});
|
|
|
|
|
}
|
2023-09-16 00:33:02 +02:00
|
|
|
void visit(AstCLocalScope* nodep) override {
|
|
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCoverInc* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->declp());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDisplay* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->displayType();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstMonitorOff* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->off();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstJumpGo* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->labelp());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTraceInc* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->declp());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeCoverOrAssert* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------
|
2022-12-23 17:32:38 +01:00
|
|
|
// AstNode direct descendants
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeRange* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2022-04-22 23:39:45 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() { //
|
2023-05-11 00:34:29 +02:00
|
|
|
m_hash += nodep->name();
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodePreSel* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClassExtends* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSelLoopVars* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDefParam* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstArg* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstParseRef* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
|
|
|
|
m_hash += nodep->expect();
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClassOrPackageRef* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->classOrPackageNodep());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSenItem* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->edgeType();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSenTree* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSFormatF* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->text();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstElabDisplay* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->displayType();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstInitItem* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstInitArray* nodep) override {
|
2021-12-11 17:29:01 +01:00
|
|
|
if (const AstAssocArrayDType* const dtypep = VN_CAST(nodep->dtypep(), AssocArrayDType)) {
|
|
|
|
|
if (nodep->defaultp()) {
|
|
|
|
|
m_hash
|
|
|
|
|
+= hashNodeAndIterate(nodep->defaultp(), HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
2021-06-13 16:05:55 +02:00
|
|
|
}
|
2021-12-11 17:29:01 +01:00
|
|
|
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) { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->getIndexDefaultedValuep(n));
|
2021-12-11 17:29:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2021-05-21 15:34:27 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPragma* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->pragType();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAttrOf* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->attrType();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFile* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2021-06-13 15:33:11 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
2022-01-05 02:19:58 +01:00
|
|
|
// 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
|
2022-01-05 02:19:58 +01:00
|
|
|
// 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.
|
2021-06-13 15:33:11 +02:00
|
|
|
m_hash += nodep->isLoose();
|
|
|
|
|
});
|
2021-05-21 15:34:27 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
m_hash += nodep->varType();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScope* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() {
|
|
|
|
|
m_hash += nodep->name();
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->aboveScopep());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarScope* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->varp());
|
|
|
|
|
iterateConstNull(nodep->scopep());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstEnumItem* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTypedef* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTypedefFwd* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstActive* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->sensesp());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCell* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
|
|
|
|
m_hash += nodep->name();
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->modp());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCellInline* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
|
|
|
|
m_hash += nodep->name();
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->scopep());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstModport* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstModportVarRef* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
|
|
|
|
m_hash += nodep->name();
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->varp());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstModportFTaskRef* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
|
|
|
|
m_hash += nodep->name();
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConstNull(nodep->ftaskp());
|
2021-05-21 15:34:27 +02:00
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstMTaskBody* nodep) override {
|
2021-09-27 04:51:11 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeProcedure* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeBlock* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPin* nodep) override {
|
2021-05-21 15:34:27 +02:00
|
|
|
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
|
|
|
|
|
m_hash += nodep->name();
|
|
|
|
|
m_hash += nodep->pinNum();
|
|
|
|
|
});
|
2021-05-21 02:41:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2022-07-30 16:01:25 +02:00
|
|
|
explicit HasherVisitor(AstNode* nodep)
|
2021-05-21 02:41:46 +02:00
|
|
|
: m_cacheInUser4{true} {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep);
|
2021-05-21 02:41:46 +02:00
|
|
|
}
|
2022-01-05 02:19:58 +01:00
|
|
|
class Uncached {};
|
|
|
|
|
HasherVisitor(const AstNode* nodep, Uncached)
|
2021-05-21 02:41:46 +02:00
|
|
|
: m_cacheInUser4{false} {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(const_cast<AstNode*>(nodep));
|
2021-05-21 02:41:46 +02:00
|
|
|
}
|
2021-05-21 15:34:27 +02:00
|
|
|
V3Hash finalHash() const { return m_hash; }
|
2022-09-16 12:22:11 +02:00
|
|
|
~HasherVisitor() override = default;
|
2021-05-21 02:41:46 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// V3Hasher methods
|
|
|
|
|
|
|
|
|
|
V3Hash V3Hasher::operator()(AstNode* nodep) const {
|
2022-01-05 02:19:58 +01:00
|
|
|
if (!nodep->user4()) HasherVisitor{nodep};
|
2022-11-20 19:11:01 +01:00
|
|
|
return V3Hash{nodep->user4()};
|
2021-05-21 02:41:46 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-27 16:54:04 +01:00
|
|
|
V3Hash V3Hasher::rehash(AstNode* nodep) const {
|
|
|
|
|
nodep->user4(0);
|
2022-07-30 16:01:25 +02:00
|
|
|
{ HasherVisitor{nodep}; }
|
2022-11-20 19:11:01 +01:00
|
|
|
return V3Hash{nodep->user4()};
|
2022-02-27 16:54:04 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-21 02:41:46 +02:00
|
|
|
V3Hash V3Hasher::uncachedHash(const AstNode* nodep) {
|
2022-01-05 02:19:58 +01:00
|
|
|
const HasherVisitor visitor{nodep, HasherVisitor::Uncached{}};
|
2021-05-21 02:41:46 +02:00
|
|
|
return visitor.finalHash();
|
|
|
|
|
}
|
2022-02-05 17:04:48 +01:00
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// This is used by the std::hash specialization for VNRef.
|
|
|
|
|
// Declared separately to avoid a circular header dependency.
|
|
|
|
|
|
2022-05-15 14:30:04 +02:00
|
|
|
size_t V3HasherUncachedHash(const AstNode& node) {
|
2022-02-05 17:04:48 +01:00
|
|
|
return static_cast<size_t>(V3Hasher::uncachedHash(&node).value());
|
|
|
|
|
}
|