diff --git a/src/V3Ast.h b/src/V3Ast.h index 9ddf48e43..4c306da80 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1972,47 +1972,121 @@ private: } while (nodep); } -public: - // Traverse subtree and call given function 'f' in pre-order on each node that has type 'T'. - // Prefer 'foreach' over simple VNVisitor that only needs to handle a single (or a few) node - // types, as it's easier to write, but more importantly, the dispatch to the operation function - // in 'foreach' should be completely predictable by branch target caches in modern CPUs, - // while it is basically unpredictable for VNVisitor. - template void foreach (std::function f) { + template + static bool predicateImpl( + // Using std::conditional for const correctness in the public 'foreach' functions + typename std::conditional::value, const AstNode*, AstNode*>::type + nodep, + std::function p) { + + // Note: Using a loop to iterate the nextp() chain, instead of tail recursion, because + // debug builds don't eliminate tail calls, causing stack overflow on long lists of nodes. + do { + // Prefetch children and next + ASTNODE_PREFETCH(nodep->op1p()); + ASTNODE_PREFETCH(nodep->op2p()); + ASTNODE_PREFETCH(nodep->op3p()); + ASTNODE_PREFETCH(nodep->op4p()); + if /* TODO: 'constexpr' in C++17 */ (VisitNext) ASTNODE_PREFETCH(nodep->nextp()); + + // Apply function in pre-order + if (privateTypeTest::type>(nodep)) { + if (p(static_cast(nodep)) != Default) return !Default; + } + + // Traverse children (including their 'nextp()' chains), unless futile + if (mayBeUnder::type>(nodep)) { + if (AstNode* const op1p = nodep->op1p()) { + if (predicateImpl(op1p, p) != Default) return !Default; + } + if (AstNode* const op2p = nodep->op2p()) { + if (predicateImpl(op2p, p) != Default) return !Default; + } + if (AstNode* const op3p = nodep->op3p()) { + if (predicateImpl(op3p, p) != Default) return !Default; + } + if (AstNode* const op4p = nodep->op4p()) { + if (predicateImpl(op4p, p) != Default) return !Default; + } + } + + // Traverse 'nextp()' chain if requested + if /* TODO: 'constexpr' in C++17 */ (VisitNext) { + nodep = nodep->nextp(); + } else { + break; + } + } while (nodep); + + return Default; + } + + template constexpr static void checkTypeParameter() { static_assert(!std::is_const::value, "Type parameter 'T_Node' should not be const qualified"); static_assert(std::is_base_of::value, "Type parameter 'T_Node' must be a subtype of AstNode"); + } + +public: + // Traverse subtree and call given function 'f' in pre-order on each node that has type + // 'T_Node'. Prefer 'foreach' over simple VNVisitor that only needs to handle a single (or a + // few) node types, as it's easier to write, but more importantly, the dispatch to the + // operation function in 'foreach' should be completely predictable by branch target caches in + // modern CPUs, while it is basically unpredictable for VNVisitor. + template void foreach (std::function f) { + checkTypeParameter(); foreachImpl(this, f); } // Same as above, but for 'const' nodes template void foreach (std::function f) const { - static_assert(!std::is_const::value, - "Type parameter 'T_Node' should not be const qualified"); - static_assert(std::is_base_of::value, - "Type parameter 'T_Node' must be a subtype of AstNode"); + checkTypeParameter(); foreachImpl(this, f); } // Same as 'foreach' but also follows 'this->nextp()' template void foreachAndNext(std::function f) { - static_assert(!std::is_const::value, - "Type parameter 'T_Node' should not be const qualified"); - static_assert(std::is_base_of::value, - "Type parameter 'T_Node' must be a subtype of AstNode"); + checkTypeParameter(); foreachImpl(this, f); } // Same as 'foreach' but also follows 'this->nextp()' template void foreachAndNext(std::function f) const { - static_assert(!std::is_const::value, - "Type parameter 'T_Node' should not be const qualified"); - static_assert(std::is_base_of::value, - "Type parameter 'T_Node' must be a subtype of AstNode"); + checkTypeParameter(); foreachImpl(this, f); } + // Given a predicate function 'p' return true if and only if there exists a node of type + // 'T_Node' that satisfies the predicate 'p'. Returns false if no node of type 'T_Node' is + // present. Traversal is performed in some arbitrary order and is terminated as soon as the + // result can be determined. + template bool exists(std::function p) { + checkTypeParameter(); + return predicateImpl(this, p); + } + + // Same as above, but for 'const' nodes + template void exists(std::function p) const { + checkTypeParameter(); + return predicateImpl(this, p); + } + + // Given a predicate function 'p' return true if and only if all nodes of type + // 'T_Node' satisfy the predicate 'p'. Returns true if no node of type 'T_Node' is + // present. Traversal is performed in some arbitrary order and is terminated as soon as the + // result can be determined. + template bool forall(std::function p) { + checkTypeParameter(); + return predicateImpl(this, p); + } + + // Same as above, but for 'const' nodes + template void forall(std::function p) const { + checkTypeParameter(); + return predicateImpl(this, p); + } + int nodeCount() const { // TODO: this should really return size_t, but need to fix use sites int count = 0;