2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Dead code elimination
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2024-01-01 09:19:59 +01:00
|
|
|
// Copyright 2003-2024 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DEAD TRANSFORMATIONS:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Remove any unreferenced modules
|
|
|
|
|
// Remove any unreferenced variables
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2012-05-03 02:53:19 +02:00
|
|
|
// TODO: A graph would make the process of circular and interlinked
|
|
|
|
|
// dependencies easier to resolve.
|
2009-11-08 03:05:02 +01:00
|
|
|
// NOTE: If redo this, consider using maybePointedTo()/broken() ish scheme
|
|
|
|
|
// instead of needing as many visitors.
|
2016-05-12 03:44:55 +02:00
|
|
|
//
|
|
|
|
|
// The following nodes have package pointers and are cleaned up here:
|
|
|
|
|
// AstRefDType, AstEnumItemRef, AstNodeVarRef, AstNodeFTask
|
|
|
|
|
// These have packagep but will not exist at this stage
|
2020-07-11 16:29:15 +02:00
|
|
|
// AstPackageImport, AstDot, AstClassOrPackageRef
|
2016-05-12 03:44:55 +02:00
|
|
|
//
|
|
|
|
|
// Note on packagep: After the V3Scope/V3LinkDotScoped stage, package links
|
|
|
|
|
// are no longer used, but their presence prevents us from removing empty
|
|
|
|
|
// packages. As the links as no longer used after V3Scope, we remove them
|
2020-11-25 03:28:04 +01:00
|
|
|
// here after scoping to allow more dead node removal.
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2023-10-18 04:50:27 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3Dead.h"
|
|
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <vector>
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Dead state, as a visitor of each AstNode
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class DeadVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// Entire Netlist:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstNodeModule::user1() -> int. Count of number of cells referencing this module.
|
|
|
|
|
// AstVar::user1() -> int. Count of number of references
|
|
|
|
|
// AstVarScope::user1() -> int. Count of number of references
|
|
|
|
|
// AstNodeDType::user1() -> int. Count of number of references
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2006-09-26 19:05:08 +02:00
|
|
|
// TYPES
|
2021-03-13 00:10:45 +01:00
|
|
|
using AssignMap = std::multimap<AstVarScope*, AstNodeAssign*>;
|
2006-09-26 19:05:08 +02:00
|
|
|
|
2023-05-27 17:46:25 +02:00
|
|
|
// STATE - across all visitors
|
|
|
|
|
const bool m_elimUserVars; // Allow removal of user's vars
|
|
|
|
|
const bool m_elimDTypes; // Allow removal of DTypes
|
|
|
|
|
const bool m_elimCells; // Allow removal of Cells
|
2020-04-15 13:58:34 +02:00
|
|
|
// List of all encountered to avoid another loop through tree
|
|
|
|
|
std::vector<AstVar*> m_varsp;
|
|
|
|
|
std::vector<AstNode*> m_dtypesp;
|
|
|
|
|
std::vector<AstVarScope*> m_vscsp;
|
|
|
|
|
std::vector<AstScope*> m_scopesp;
|
|
|
|
|
std::vector<AstCell*> m_cellsp;
|
|
|
|
|
std::vector<AstClass*> m_classesp;
|
2022-12-21 01:22:42 +01:00
|
|
|
std::vector<AstTypedef*> m_typedefsp;
|
2020-04-15 13:58:34 +02:00
|
|
|
AssignMap m_assignMap; // List of all simple assignments for each variable
|
2020-11-25 03:28:04 +01:00
|
|
|
bool m_sideEffect = false; // Side effects discovered in assign RHS
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2023-05-27 17:46:25 +02:00
|
|
|
// STATE - for current visit position (use VL_RESTORER)
|
2023-05-27 18:43:40 +02:00
|
|
|
bool m_inAssign = false; // Currently in an assign
|
2023-05-27 17:46:25 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
|
|
|
|
AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
2012-04-29 16:14:13 +02:00
|
|
|
|
2023-11-13 04:08:08 +01:00
|
|
|
void deleting(AstNode* nodep) {
|
|
|
|
|
UINFO(9, " deleting " << nodep << endl);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-03 18:10:29 +01:00
|
|
|
void checkAll(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstNode* const subnodep = nodep->dtypep()) subnodep->user1Inc();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstNode* const subnodep = nodep->getChildDTypep()) subnodep->user1Inc();
|
2012-04-29 16:14:13 +02:00
|
|
|
}
|
2022-07-30 17:52:35 +02:00
|
|
|
void checkVarRef(AstNodeVarRef* nodep) const {
|
2020-11-25 03:56:03 +01:00
|
|
|
if (nodep->classOrPackagep() && m_elimCells) nodep->classOrPackagep(nullptr);
|
2020-11-25 03:34:11 +01:00
|
|
|
}
|
2012-04-29 16:14:13 +02:00
|
|
|
void checkDType(AstNodeDType* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->generic() // Don't remove generic types
|
|
|
|
|
&& m_elimDTypes // dtypes stick around until post-widthing
|
2018-02-02 03:32:58 +01:00
|
|
|
&& !VN_IS(nodep, MemberDType) // Keep member names iff upper type exists
|
2021-12-13 00:10:52 +01:00
|
|
|
&& !nodep->undead() // VoidDType or something Netlist points to
|
2020-04-15 13:58:34 +02:00
|
|
|
) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_dtypesp.push_back(nodep);
|
|
|
|
|
}
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstNode* const subnodep = nodep->virtRefDTypep()) subnodep->user1Inc();
|
|
|
|
|
if (AstNode* const subnodep = nodep->virtRefDType2p()) subnodep->user1Inc();
|
2012-03-03 18:10:29 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-11-25 04:46:02 +01:00
|
|
|
if (m_modp) m_modp->user1Inc(); // e.g. Class under Package
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2023-05-27 17:46:25 +02:00
|
|
|
m_modp = nodep;
|
|
|
|
|
if (!nodep->dead()) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkAll(nodep);
|
|
|
|
|
if (AstClass* const classp = VN_CAST(nodep, Class)) {
|
|
|
|
|
if (classp->extendsp()) classp->extendsp()->user1Inc();
|
|
|
|
|
if (classp->classOrPackagep()) classp->classOrPackagep()->user1Inc();
|
|
|
|
|
m_classesp.push_back(classp);
|
|
|
|
|
// TODO we don't reclaim dead classes yet - graph implementation instead?
|
|
|
|
|
classp->user1Inc();
|
2020-01-20 19:27:27 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2014-11-06 23:53:01 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkAll(nodep);
|
|
|
|
|
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
2016-05-12 03:44:55 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScope* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkAll(nodep);
|
|
|
|
|
if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc();
|
2020-04-05 15:30:23 +02:00
|
|
|
// Class packages might have no children, but need to remain as
|
|
|
|
|
// long as the class they refer to is needed
|
|
|
|
|
if (VN_IS(m_modp, Class) || VN_IS(m_modp, ClassPackage)) nodep->user1Inc();
|
2022-05-15 17:03:32 +02:00
|
|
|
if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_scopesp.push_back(nodep);
|
|
|
|
|
}
|
2016-05-12 03:44:55 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCell* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkAll(nodep);
|
|
|
|
|
m_cellsp.push_back(nodep);
|
|
|
|
|
nodep->modp()->user1Inc();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2016-05-12 03:44:55 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2020-11-25 03:34:11 +01:00
|
|
|
// Note NodeAssign skips calling this in some cases
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkAll(nodep);
|
2020-11-25 03:34:11 +01:00
|
|
|
checkVarRef(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->varScopep()) {
|
|
|
|
|
nodep->varScopep()->user1Inc();
|
|
|
|
|
nodep->varScopep()->varp()->user1Inc();
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->varp()) nodep->varp()->user1Inc();
|
2020-11-25 03:56:03 +01:00
|
|
|
if (nodep->classOrPackagep()) nodep->classOrPackagep()->user1Inc();
|
2009-11-08 03:05:02 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkAll(nodep);
|
2020-11-25 03:56:03 +01:00
|
|
|
if (nodep->classOrPackagep()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_elimCells) {
|
2020-11-25 03:56:03 +01:00
|
|
|
nodep->classOrPackagep(nullptr);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2020-11-25 03:56:03 +01:00
|
|
|
nodep->classOrPackagep()->user1Inc();
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-11-08 03:05:02 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstMethodCall* nodep) override {
|
2020-03-07 18:52:11 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkAll(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstRefDType* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkDType(nodep);
|
|
|
|
|
checkAll(nodep);
|
2020-05-24 20:22:06 +02:00
|
|
|
UASSERT_OBJ(!(m_elimCells && nodep->typedefp()), nodep,
|
|
|
|
|
"RefDType should point to data type before typedefs removed");
|
2020-11-25 03:56:03 +01:00
|
|
|
if (nodep->classOrPackagep()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_elimCells) {
|
2020-11-25 03:56:03 +01:00
|
|
|
nodep->classOrPackagep(nullptr);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2020-11-25 03:56:03 +01:00
|
|
|
nodep->classOrPackagep()->user1Inc();
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClassRefDType* nodep) override {
|
2020-04-05 15:30:23 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkDType(nodep);
|
|
|
|
|
checkAll(nodep);
|
2020-11-25 03:56:03 +01:00
|
|
|
if (nodep->classOrPackagep()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_elimCells) {
|
2020-11-25 03:56:03 +01:00
|
|
|
nodep->classOrPackagep(nullptr);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2020-11-25 03:56:03 +01:00
|
|
|
nodep->classOrPackagep()->user1Inc();
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2020-04-05 15:30:23 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->classp()) nodep->classp()->user1Inc();
|
2020-04-05 15:30:23 +02:00
|
|
|
}
|
2022-10-20 12:31:00 +02:00
|
|
|
void visit(AstIfaceRefDType* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkDType(nodep);
|
|
|
|
|
checkAll(nodep);
|
|
|
|
|
if (nodep->modportp()) {
|
|
|
|
|
if (m_elimCells) {
|
|
|
|
|
nodep->modportp(nullptr);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->modportp()->user1Inc();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (nodep->ifaceViaCellp()) nodep->ifaceViaCellp()->user1Inc();
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeDType* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkDType(nodep);
|
|
|
|
|
checkAll(nodep);
|
2012-04-29 16:14:13 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstEnumItemRef* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkAll(nodep);
|
2020-11-25 03:56:03 +01:00
|
|
|
if (nodep->classOrPackagep()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_elimCells) {
|
2020-11-25 03:56:03 +01:00
|
|
|
nodep->classOrPackagep(nullptr);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2020-11-25 03:56:03 +01:00
|
|
|
nodep->classOrPackagep()->user1Inc();
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
checkAll(nodep);
|
2016-05-12 03:44:55 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstMemberSel* nodep) override {
|
2020-03-07 18:52:11 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (nodep->varp()) nodep->varp()->user1Inc();
|
|
|
|
|
if (nodep->fromp()->dtypep()) nodep->fromp()->dtypep()->user1Inc(); // classref
|
|
|
|
|
checkAll(nodep);
|
|
|
|
|
}
|
2022-12-21 01:22:42 +01:00
|
|
|
void visit(AstStructSel* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (nodep->fromp()->dtypep()) nodep->fromp()->dtypep()->user1Inc(); // structdtype
|
|
|
|
|
checkAll(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstModport* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_elimCells) {
|
|
|
|
|
if (!nodep->varsp()) {
|
2023-11-13 04:08:08 +01:00
|
|
|
deleting(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
checkAll(nodep);
|
2011-04-14 01:34:14 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSelLoopVars* nodep) override {
|
2021-12-11 21:06:33 +01:00
|
|
|
// Var under a SelLoopVars means we haven't called V3Width to remove them yet
|
|
|
|
|
VL_RESTORER(m_selloopvarsp);
|
|
|
|
|
m_selloopvarsp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
checkAll(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTypedef* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2022-12-21 01:22:42 +01:00
|
|
|
m_typedefsp.push_back(nodep);
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Don't let packages with only public variables disappear
|
|
|
|
|
// Normal modules may disappear, e.g. if they are parameterized then removed
|
2018-02-02 03:32:58 +01:00
|
|
|
if (nodep->attrPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
|
2014-11-07 13:50:11 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarScope* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkAll(nodep);
|
|
|
|
|
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (mightElimVar(nodep->varp())) m_vscsp.push_back(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkAll(nodep);
|
2018-02-02 03:32:58 +01:00
|
|
|
if (nodep->isSigPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
|
2021-12-11 21:06:33 +01:00
|
|
|
if (m_selloopvarsp) nodep->user1Inc();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (mightElimVar(nodep)) m_varsp.push_back(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// See if simple assignments to variables may be eliminated because
|
|
|
|
|
// that variable is never used.
|
|
|
|
|
// Similar code in V3Life
|
2023-05-27 18:43:40 +02:00
|
|
|
const bool assignInAssign = m_inAssign; // Might be Assign(..., ExprStmt(Assign), ...)
|
2020-11-25 03:34:11 +01:00
|
|
|
{
|
2023-05-27 18:43:40 +02:00
|
|
|
VL_RESTORER(m_inAssign);
|
|
|
|
|
VL_RESTORER(m_sideEffect);
|
|
|
|
|
m_inAssign = true;
|
2020-11-25 03:34:11 +01:00
|
|
|
m_sideEffect = false;
|
|
|
|
|
iterateAndNextNull(nodep->rhsp());
|
|
|
|
|
checkAll(nodep);
|
|
|
|
|
// Has to be direct assignment without any EXTRACTing.
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarRef* const varrefp = VN_CAST(nodep->lhsp(), VarRef);
|
2023-12-22 22:26:51 +01:00
|
|
|
if (varrefp && !m_sideEffect && v3Global.opt.fDeadAssigns()
|
2020-11-25 03:34:11 +01:00
|
|
|
&& varrefp->varScopep()) { // For simplicity, we only remove post-scoping
|
2020-12-19 00:24:47 +01:00
|
|
|
m_assignMap.emplace(varrefp->varScopep(), nodep);
|
2020-11-25 03:34:11 +01:00
|
|
|
checkAll(varrefp); // Must track reference to dtype()
|
|
|
|
|
checkVarRef(varrefp);
|
|
|
|
|
} else { // Track like any other statement
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
}
|
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
|
|
|
iterateNull(nodep->timingControlp());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2023-05-27 18:43:40 +02:00
|
|
|
if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize
|
2006-09-26 19:05:08 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//-----
|
2022-12-23 13:34:49 +01:00
|
|
|
void visit(AstClockingItem* nodep) override {
|
|
|
|
|
// Prevent V3Dead from deleting clockvars that are seemingly dead before V3AssertPre. Later
|
|
|
|
|
// the vars will be moved to the containing module so if they are actually dead they will
|
|
|
|
|
// still get deleted.
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->isOutputter()) m_sideEffect = true;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
checkAll(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// METHODS
|
2022-12-21 01:22:42 +01:00
|
|
|
void deadCheckTypedefs() {
|
|
|
|
|
for (AstTypedef* typedefp : m_typedefsp) {
|
|
|
|
|
if (shouldDeleteTypedef(typedefp)) {
|
2023-11-13 04:08:08 +01:00
|
|
|
deleting(typedefp);
|
2022-12-21 01:22:42 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
checkAll(typedefp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool shouldDeleteTypedef(AstTypedef* typedefp) {
|
2023-01-28 04:41:12 +01:00
|
|
|
if (auto* const structp = VN_CAST(typedefp->subDTypep(), NodeUOrStructDType)) {
|
2022-12-21 01:22:42 +01:00
|
|
|
if (structp->user1() && !structp->packed()) return false;
|
|
|
|
|
}
|
|
|
|
|
return m_elimCells && !typedefp->attrPublic();
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void deadCheckMod() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Kill any unused modules
|
|
|
|
|
// V3LinkCells has a graph that is capable of this too, but we need to do it
|
|
|
|
|
// after we've done all the generate blocks
|
2020-04-15 13:58:34 +02:00
|
|
|
for (bool retry = true; retry;) {
|
2019-05-19 22:13:13 +02:00
|
|
|
retry = false;
|
|
|
|
|
AstNodeModule* nextmodp;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp = nextmodp) {
|
2021-10-22 14:56:48 +02:00
|
|
|
nextmodp = VN_AS(modp->nextp(), NodeModule);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (modp->dead()
|
|
|
|
|
|| (modp->level() > 2 && modp->user1() == 0 && !modp->internal())) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// > 2 because L1 is the wrapper, L2 is the top user module
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " Dead module " << modp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// And its children may now be killable too; correct counts
|
|
|
|
|
// Recurse, as cells may not be directly under the module but in a generate
|
|
|
|
|
if (!modp->dead()) { // If was dead didn't increment user1's
|
2022-10-20 14:48:44 +02:00
|
|
|
modp->foreach([](const AstCell* cellp) { //
|
2022-01-09 23:34:10 +01:00
|
|
|
cellp->modp()->user1Inc(-1);
|
|
|
|
|
});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2023-11-13 04:08:08 +01:00
|
|
|
deleting(modp);
|
2019-05-19 22:13:13 +02:00
|
|
|
retry = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-07-30 17:52:35 +02:00
|
|
|
bool mightElimVar(AstVar* nodep) const {
|
2020-07-02 13:35:55 +02:00
|
|
|
if (nodep->isSigPublic()) return false; // Can't elim publics!
|
2023-12-05 04:11:07 +01:00
|
|
|
if (nodep->isIO() || nodep->isClassMember() || nodep->sensIfacep()) return false;
|
2020-07-02 13:35:55 +02:00
|
|
|
if (nodep->isTemp() && !nodep->isTrace()) return true;
|
|
|
|
|
return m_elimUserVars; // Post-Trace can kill most anything
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2016-05-12 03:44:55 +02:00
|
|
|
|
|
|
|
|
void deadCheckScope() {
|
2020-04-15 13:58:34 +02:00
|
|
|
for (bool retry = true; retry;) {
|
2019-05-19 22:13:13 +02:00
|
|
|
retry = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (std::vector<AstScope*>::iterator it = m_scopesp.begin(); it != m_scopesp.end();
|
|
|
|
|
++it) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstScope* const scp = *it;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!scp) continue;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (scp->user1() == 0) {
|
2018-07-19 03:25:21 +02:00
|
|
|
UINFO(4, " Dead AstScope " << scp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
scp->aboveScopep()->user1Inc(-1);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (scp->dtypep()) scp->dtypep()->user1Inc(-1);
|
2023-11-13 04:08:08 +01:00
|
|
|
deleting(scp);
|
2020-08-15 16:12:55 +02:00
|
|
|
*it = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
retry = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-12 03:44:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void deadCheckCells() {
|
2020-08-16 17:43:49 +02:00
|
|
|
for (AstCell* cellp : m_cellsp) {
|
2023-12-22 22:26:51 +01:00
|
|
|
if (cellp->user1() == 0 && !cellp->modp()->stmtsp() && v3Global.opt.fDeadCells()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
cellp->modp()->user1Inc(-1);
|
2023-11-13 04:08:08 +01:00
|
|
|
deleting(cellp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2016-05-12 03:44:55 +02:00
|
|
|
}
|
2020-04-05 15:30:23 +02:00
|
|
|
void deadCheckClasses() {
|
|
|
|
|
for (bool retry = true; retry;) {
|
|
|
|
|
retry = false;
|
2020-11-11 04:10:38 +01:00
|
|
|
for (auto& itr : m_classesp) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstClass* const nodep = itr) { // nullptr if deleted earlier
|
2020-04-05 15:30:23 +02:00
|
|
|
if (nodep->user1() == 0) {
|
|
|
|
|
if (nodep->extendsp()) nodep->extendsp()->user1Inc(-1);
|
2020-11-25 03:56:03 +01:00
|
|
|
if (nodep->classOrPackagep()) nodep->classOrPackagep()->user1Inc(-1);
|
2023-11-13 04:08:08 +01:00
|
|
|
deleting(nodep);
|
2020-11-11 04:10:38 +01:00
|
|
|
itr = nullptr;
|
2020-04-05 15:30:23 +02:00
|
|
|
retry = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-12 03:44:55 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void deadCheckVar() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Delete any unused varscopes
|
2020-08-16 17:43:49 +02:00
|
|
|
for (AstVarScope* vscp : m_vscsp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (vscp->user1() == 0) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " Dead " << vscp << endl);
|
2021-11-26 23:55:36 +01:00
|
|
|
const std::pair<AssignMap::iterator, AssignMap::iterator> eqrange
|
2019-05-19 22:13:13 +02:00
|
|
|
= m_assignMap.equal_range(vscp);
|
|
|
|
|
for (AssignMap::iterator itr = eqrange.first; itr != eqrange.second; ++itr) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNodeAssign* const assp = itr->second;
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " Dead assign " << assp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
assp->dtypep()->user1Inc(-1);
|
2023-11-13 04:08:08 +01:00
|
|
|
deleting(assp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
if (vscp->scopep()) vscp->scopep()->user1Inc(-1);
|
|
|
|
|
vscp->dtypep()->user1Inc(-1);
|
2023-11-13 04:08:08 +01:00
|
|
|
deleting(vscp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
for (bool retry = true; retry;) {
|
2019-05-19 22:13:13 +02:00
|
|
|
retry = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (std::vector<AstVar*>::iterator it = m_varsp.begin(); it != m_varsp.end(); ++it) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const varp = *it;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!varp) continue;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (varp->user1() == 0) {
|
2018-07-19 03:25:21 +02:00
|
|
|
UINFO(4, " Dead " << varp << endl);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (varp->dtypep()) varp->dtypep()->user1Inc(-1);
|
2023-11-13 04:08:08 +01:00
|
|
|
deleting(varp);
|
2020-08-15 16:12:55 +02:00
|
|
|
*it = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
retry = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
for (std::vector<AstNode*>::iterator it = m_dtypesp.begin(); it != m_dtypesp.end(); ++it) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if ((*it)->user1() == 0) {
|
|
|
|
|
// It's possible that there if a reference to each individual member, but
|
|
|
|
|
// not to the dtype itself. Check and don't remove the parent dtype if
|
|
|
|
|
// members are still alive.
|
2023-11-13 04:08:08 +01:00
|
|
|
if (const AstNodeUOrStructDType* const classp
|
|
|
|
|
= VN_CAST((*it), NodeUOrStructDType)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
bool cont = true;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstMemberDType* memberp = classp->membersp(); memberp;
|
2021-10-22 14:56:48 +02:00
|
|
|
memberp = VN_AS(memberp->nextp(), MemberDType)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (memberp->user1() != 0) {
|
|
|
|
|
cont = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!cont) continue;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2023-11-13 04:08:08 +01:00
|
|
|
deleting(*it);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-01 16:48:37 +02:00
|
|
|
void preserveTopIfaces(AstNetlist* rootp) {
|
|
|
|
|
for (AstNodeModule* modp = rootp->modulesp(); modp && modp->level() <= 2;
|
2022-10-01 16:53:40 +02:00
|
|
|
modp = VN_AS(modp->nextp(), NodeModule)) {
|
2022-10-01 16:48:37 +02:00
|
|
|
for (AstNode* subnodep = modp->stmtsp(); subnodep; subnodep = subnodep->nextp()) {
|
|
|
|
|
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
|
|
|
|
|
if (varp->isIfaceRef()) {
|
|
|
|
|
const AstNodeDType* const subtypep = varp->subDTypep();
|
|
|
|
|
const AstIfaceRefDType* ifacerefp = nullptr;
|
|
|
|
|
if (VN_IS(subtypep, IfaceRefDType)) {
|
|
|
|
|
ifacerefp = VN_AS(varp->subDTypep(), IfaceRefDType);
|
2022-10-01 16:53:40 +02:00
|
|
|
} else if (VN_IS(subtypep, BracketArrayDType)) {
|
|
|
|
|
const AstBracketArrayDType* const arrp
|
|
|
|
|
= VN_AS(subtypep, BracketArrayDType);
|
2022-10-01 16:48:37 +02:00
|
|
|
const AstNodeDType* const arrsubtypep = arrp->subDTypep();
|
|
|
|
|
if (VN_IS(arrsubtypep, IfaceRefDType)) {
|
|
|
|
|
ifacerefp = VN_AS(arrsubtypep, IfaceRefDType);
|
|
|
|
|
}
|
2022-10-01 16:53:40 +02:00
|
|
|
} else if (VN_IS(subtypep, UnpackArrayDType)) {
|
|
|
|
|
const AstUnpackArrayDType* const arrp
|
|
|
|
|
= VN_AS(subtypep, UnpackArrayDType);
|
2022-10-01 16:48:37 +02:00
|
|
|
const AstNodeDType* const arrsubtypep = arrp->subDTypep();
|
|
|
|
|
if (VN_IS(arrsubtypep, IfaceRefDType)) {
|
|
|
|
|
ifacerefp = VN_AS(arrsubtypep, IfaceRefDType);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-01 16:53:40 +02:00
|
|
|
if (ifacerefp && !ifacerefp->cellp()
|
|
|
|
|
&& (ifacerefp->ifacep()->user1() == 0)) {
|
2022-10-01 16:48:37 +02:00
|
|
|
ifacerefp->ifacep()->user1(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-04-15 13:58:34 +02:00
|
|
|
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes,
|
2022-10-01 16:48:37 +02:00
|
|
|
bool elimCells, bool elimTopIfaces)
|
2020-11-25 03:28:04 +01:00
|
|
|
: m_elimUserVars{elimUserVars}
|
|
|
|
|
, m_elimDTypes{elimDTypes}
|
|
|
|
|
, m_elimCells{elimCells} {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Prepare to remove some datatypes
|
|
|
|
|
nodep->typeTablep()->clearCache();
|
|
|
|
|
// Operate on whole netlist
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2022-07-14 13:35:44 +02:00
|
|
|
|
|
|
|
|
if (AstVarScope* const vscp = nodep->dpiExportTriggerp()) {
|
|
|
|
|
vscp->user1Inc();
|
|
|
|
|
vscp->varp()->user1Inc();
|
|
|
|
|
}
|
2016-05-12 03:44:55 +02:00
|
|
|
|
2022-12-21 01:22:42 +01:00
|
|
|
deadCheckTypedefs();
|
2019-05-19 22:13:13 +02:00
|
|
|
deadCheckVar();
|
2019-09-09 13:50:21 +02:00
|
|
|
// We only eliminate scopes when in a flattened structure
|
2019-05-19 22:13:13 +02:00
|
|
|
// Otherwise we have no easy way to know if a scope is used
|
|
|
|
|
if (elimScopes) deadCheckScope();
|
|
|
|
|
if (elimCells) deadCheckCells();
|
2020-04-05 15:30:23 +02:00
|
|
|
deadCheckClasses();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Modules after vars, because might be vars we delete inside a mod we delete
|
2022-10-01 16:48:37 +02:00
|
|
|
if (!elimTopIfaces) preserveTopIfaces(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
deadCheckMod();
|
2016-05-12 03:44:55 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// We may have removed some datatypes, cleanup
|
|
|
|
|
nodep->typeTablep()->repairCache();
|
2023-10-09 11:50:31 +02:00
|
|
|
VIsCached::clearCacheTree(); // Removing assignments may affect isPure
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~DeadVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Dead class functions
|
|
|
|
|
|
2012-04-29 14:24:32 +02:00
|
|
|
void V3Dead::deadifyModules(AstNetlist* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2022-10-01 16:53:40 +02:00
|
|
|
{
|
|
|
|
|
DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported()};
|
|
|
|
|
} // Destruct before checking
|
2023-05-04 00:04:10 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeLevel() >= 6);
|
2012-04-29 14:24:32 +02:00
|
|
|
}
|
2016-05-12 03:44:55 +02:00
|
|
|
|
2012-04-29 14:24:32 +02:00
|
|
|
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2022-10-01 16:48:37 +02:00
|
|
|
{ DeadVisitor{nodep, false, true, false, false, false}; } // Destruct before checking
|
2023-05-04 00:04:10 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTreeLevel() >= 3);
|
2016-05-12 03:44:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2022-10-01 16:48:37 +02:00
|
|
|
{ DeadVisitor{nodep, false, true, true, false, false}; } // Destruct before checking
|
2023-05-04 00:04:10 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTreeLevel() >= 3);
|
2012-04-29 14:24:32 +02:00
|
|
|
}
|
2016-05-12 03:44:55 +02:00
|
|
|
|
2012-04-29 14:24:32 +02:00
|
|
|
void V3Dead::deadifyAll(AstNetlist* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2022-10-01 16:48:37 +02:00
|
|
|
{ DeadVisitor{nodep, true, true, false, true, false}; } // Destruct before checking
|
2023-05-04 00:04:10 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2016-05-12 03:44:55 +02:00
|
|
|
|
|
|
|
|
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2022-10-01 16:48:37 +02:00
|
|
|
{ DeadVisitor{nodep, true, true, true, true, false}; } // Destruct before checking
|
2023-05-04 00:04:10 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeLevel() >= 3);
|
2016-05-12 03:44:55 +02:00
|
|
|
}
|