@@ -33,9 +33,29 @@ class HasherVisitor final : public VNVisitorConst {
33
33
// STATE
34
34
V3Hash m_hash; // Hash value accumulator
35
35
const bool m_cacheInUser4; // Use user4 to cache each V3Hash?
36
+ std::vector<AstNode*> m_stack; // Keeps track of some visited nodes to prevent
37
+ // infinite recursion
36
38
37
39
// METHODS
38
40
41
+ void guardRecursion (AstNode* const nodep, std::function<void ()>&& f) {
42
+ // Guard against infinite recursion if there's no caching
43
+ // Otherwise caching does the same but faster
44
+ if (!m_cacheInUser4) {
45
+ bool isOnStack = false ;
46
+ for (AstNode* stackNodep : m_stack) {
47
+ if (stackNodep == nodep) isOnStack = true ;
48
+ }
49
+ if (isOnStack) {
50
+ m_hash += V3Hash{1 };
51
+ return ;
52
+ }
53
+ m_stack.push_back (nodep);
54
+ f ();
55
+ m_stack.pop_back ();
56
+ }
57
+ }
58
+
39
59
V3Hash hashNodeAndIterate (AstNode* nodep, bool hashDType, bool hashChildren,
40
60
std::function<void ()>&& f) {
41
61
// See comments in visit(AstCFunc) about this breaking recursion
@@ -410,14 +430,16 @@ class HasherVisitor final : public VNVisitorConst {
410
430
});
411
431
}
412
432
void visit (AstCFunc* nodep) override {
413
- m_hash += hashNodeAndIterate (nodep, HASH_DTYPE, HASH_CHILDREN, [this , nodep]() { //
414
- // We might be in a recursive function, if so on *second* call
415
- // here we need to break what would be an infinite loop.
416
- nodep->user4 (V3Hash{1 }.value ()); // Set this "first" call
417
- // So that a second call will then exit hashNodeAndIterate
418
- // Having a constant in the hash just means the recursion will
419
- // end, it shouldn't change the CFunc having a unique hash itself.
420
- m_hash += nodep->isLoose ();
433
+ guardRecursion (nodep, [this , nodep]() { //
434
+ m_hash += hashNodeAndIterate (nodep, HASH_DTYPE, HASH_CHILDREN, [this , nodep]() { //
435
+ // We might be in a recursive function, if so on *second* call
436
+ // here we need to break what would be an infinite loop.
437
+ if (m_cacheInUser4) nodep->user4 (V3Hash{1 }.value ()); // Set this "first" call
438
+ // So that a second call will then exit hashNodeAndIterate
439
+ // Having a constant in the hash just means the recursion will
440
+ // end, it shouldn't change the CFunc having a unique hash itself.
441
+ m_hash += nodep->isLoose ();
442
+ });
421
443
});
422
444
}
423
445
void visit (AstVar* nodep) override {
@@ -469,8 +491,12 @@ class HasherVisitor final : public VNVisitorConst {
469
491
[this , nodep]() { m_hash += nodep->name (); });
470
492
}
471
493
void visit (AstNodeFTask* nodep) override {
472
- m_hash += hashNodeAndIterate (nodep, HASH_DTYPE, HASH_CHILDREN, [this , nodep]() { //
473
- m_hash += nodep->name ();
494
+ guardRecursion (nodep, [this , nodep]() { //
495
+ m_hash += hashNodeAndIterate (nodep, HASH_DTYPE, HASH_CHILDREN, [this , nodep]() { //
496
+ m_hash += nodep->name ();
497
+ // See comments in AstCFunc
498
+ if (m_cacheInUser4) nodep->user4 (V3Hash{1 }.value ());
499
+ });
474
500
});
475
501
}
476
502
void visit (AstModport* nodep) override {
0 commit comments