verilator/src/V3Depth.cpp

165 lines
5.7 KiB
C++
Raw Normal View History

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Prevent very deep expressions
//
2019-11-08 04:33:59 +01:00
// 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
//
//*************************************************************************
// V3Depth's Transformations:
//
// Each module:
// For each wide OP, assign a temporary variable.
// For each deep expression, assign expression to temporary.
// Each CFunc:
// Any statements that need "this" are marked non-static
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3Depth.h"
#include "V3UniqueNames.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
class DepthVisitor final : public VNVisitor {
// NODE STATE
// STATE - for current visit position (use VL_RESTORER)
AstCFunc* m_cfuncp = nullptr; // Current block
AstMTaskBody* m_mtaskbodyp = nullptr; // Current mtaskbody
AstNode* m_stmtp = nullptr; // Current statement
int m_depth = 0; // How deep in an expression
int m_maxdepth = 0; // Maximum depth in an expression
V3UniqueNames m_tempNames; // For generating unique temporary variable names
// METHODS
2009-01-21 22:56:50 +01:00
void createDeepTemp(AstNodeExpr* nodep) {
UINFO(6, " Deep " << nodep);
// UINFOTREE(9, nodep, "", "deep");
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::STMTTEMP,
m_tempNames.get(nodep), nodep->dtypep()};
if (m_cfuncp) {
m_cfuncp->addInitsp(varp);
} else if (m_mtaskbodyp) {
m_mtaskbodyp->addStmtsFirstp(varp);
} else {
nodep->v3fatalSrc("Deep expression not under a function");
}
// Replace node tree with reference to var
AstVarRef* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::READ};
nodep->replaceWith(newp);
// Put assignment before the referencing statement
AstAssign* const assp = new AstAssign{
nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::WRITE}, nodep};
m_stmtp->addHereThisAsNext(assp);
}
// VISITORS
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
VL_RESTORER(m_mtaskbodyp);
VL_RESTORER(m_depth);
VL_RESTORER(m_maxdepth);
m_cfuncp = nodep;
m_mtaskbodyp = nullptr;
m_depth = 0;
m_maxdepth = 0;
m_tempNames.reset();
iterateChildren(nodep);
}
void visit(AstMTaskBody* nodep) override {
VL_RESTORER(m_cfuncp);
VL_RESTORER(m_mtaskbodyp);
VL_RESTORER(m_depth);
VL_RESTORER(m_maxdepth);
m_cfuncp = nullptr;
m_mtaskbodyp = nodep;
m_depth = 0;
m_maxdepth = 0;
// We don't reset the names, as must share across tasks
iterateChildren(nodep);
}
void visitStmt(AstNodeStmt* nodep) {
VL_RESTORER(m_stmtp);
VL_RESTORER(m_depth);
VL_RESTORER(m_maxdepth);
m_stmtp = nodep;
m_depth = 0;
m_maxdepth = 0;
iterateChildren(nodep);
}
void visit(AstNodeStmt* nodep) override { visitStmt(nodep); }
// Operators
void visit(AstNodeTermop* nodep) override {}
void visit(AstNodeExpr* nodep) override {
// We have some operator defines that use 2 parens, so += 2.
{
VL_RESTORER(m_depth);
m_depth += 2;
if (m_depth > m_maxdepth) m_maxdepth = m_depth;
iterateChildren(nodep);
}
if (m_stmtp && (v3Global.opt.compLimitParens() >= 1) // Else compiler doesn't need it
&& (m_maxdepth - m_depth) > v3Global.opt.compLimitParens()
&& !VN_IS(nodep->backp(), NodeStmt) // Not much point if we're about to use it
) {
m_maxdepth = m_depth;
createDeepTemp(nodep);
}
}
//--------------------
// Marking of non-static functions (because they might need "this")
2019-09-09 13:50:21 +02:00
// (Here instead of new visitor after V3Descope just to avoid another visitor)
void needNonStaticFunc(AstNode* nodep) {
UASSERT_OBJ(m_cfuncp, nodep, "Non-static accessor not under a function");
if (m_cfuncp->isStatic()) {
UINFO(5, "Mark non-public due to " << nodep);
m_cfuncp->isStatic(false);
}
}
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(AstCExprUser* nodep) override {
needNonStaticFunc(nodep);
iterateChildren(nodep);
}
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(AstCStmtUser* nodep) override {
needNonStaticFunc(nodep);
visitStmt(nodep);
}
//--------------------
// Default: Just iterate
void visit(AstVar*) override {} // Don't hit varrefs under vars
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
2019-09-12 13:22:22 +02:00
// CONSTRUCTORS
explicit DepthVisitor(AstNetlist* nodep)
: m_tempNames{"__Vdeeptemp"} {
iterate(nodep);
}
~DepthVisitor() override = default;
};
//######################################################################
// Depth class functions
void V3Depth::depthAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
{ DepthVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("depth", 0, dumpTreeEitherLevel() >= 6);
}