mirror of
https://github.com/verilator/verilator.git
synced 2025-01-04 05:37:48 +00:00
203 lines
6.9 KiB
C++
203 lines
6.9 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Estimate stack size to run the AST subtree.
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// Copyright 2003-2024 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
|
|
//
|
|
//*************************************************************************
|
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
#include "V3StackCount.h"
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
/// Estimate the stack byte size for executing all logic within and below a
|
|
/// given AST node. This is very rough, only for warning when stack is too
|
|
/// small.
|
|
|
|
class StackCountVisitor final : public VNVisitorConst {
|
|
// NODE STATE
|
|
// AstNode::user2() -> int. Path cost + 1,
|
|
const VNUser2InUse m_inuser2;
|
|
|
|
// MEMBERS
|
|
uint64_t m_stackSize = 0; // Running count of instructions
|
|
bool m_tracingCall = false; // Iterating into a CCall to a CFunc
|
|
bool m_ignoreRemaining = false; // Ignore remaining statements in the block
|
|
bool m_inCFunc = false; // Inside function
|
|
|
|
// TYPES
|
|
// Little class to cleanly call startVisitBase/endVisitBase
|
|
class VisitBase final {
|
|
// MEMBERS
|
|
uint32_t m_savedCount;
|
|
AstNode* const m_nodep;
|
|
StackCountVisitor* const m_visitor;
|
|
|
|
public:
|
|
// CONSTRUCTORS
|
|
VisitBase(StackCountVisitor* visitor, AstNode* nodep)
|
|
: m_nodep{nodep}
|
|
, m_visitor{visitor} {
|
|
m_savedCount = m_visitor->startVisitBase(nodep);
|
|
}
|
|
~VisitBase() { m_visitor->endVisitBase(m_savedCount, m_nodep); }
|
|
|
|
private:
|
|
VL_UNCOPYABLE(VisitBase);
|
|
};
|
|
|
|
public:
|
|
// CONSTRUCTORS
|
|
explicit StackCountVisitor(AstNode* nodep) { iterateConstNull(nodep); }
|
|
~StackCountVisitor() override = default;
|
|
|
|
// METHODS
|
|
uint32_t stackSize() const { return m_stackSize; }
|
|
|
|
private:
|
|
void reset() {
|
|
m_stackSize = 0;
|
|
m_ignoreRemaining = false;
|
|
}
|
|
uint32_t startVisitBase(AstNode* nodep) {
|
|
UASSERT_OBJ(!m_ignoreRemaining, nodep, "Should not reach here if ignoring");
|
|
|
|
// Save the count, and add it back in during ~VisitBase This allows
|
|
// debug prints to show local cost of each subtree, so we can see a
|
|
// hierarchical view of the cost when in debug mode.
|
|
const uint32_t savedCount = m_stackSize;
|
|
m_stackSize = 0;
|
|
return savedCount;
|
|
}
|
|
void endVisitBase(uint32_t savedCount, AstNode* nodep) {
|
|
UINFO(8, "cost " << std::setw(6) << std::left << m_stackSize << " " << nodep << endl);
|
|
if (!m_ignoreRemaining) m_stackSize += savedCount;
|
|
}
|
|
|
|
// VISITORS
|
|
void visit(AstNodeIf* nodep) override {
|
|
if (m_ignoreRemaining) return;
|
|
const VisitBase vb{this, nodep};
|
|
iterateAndNextConstNull(nodep->condp());
|
|
const uint32_t savedCount = m_stackSize;
|
|
|
|
UINFO(8, "thensp:\n");
|
|
reset();
|
|
iterateAndNextConstNull(nodep->thensp());
|
|
uint32_t ifCount = m_stackSize;
|
|
if (nodep->branchPred().unlikely()) ifCount = 0;
|
|
|
|
UINFO(8, "elsesp:\n");
|
|
reset();
|
|
iterateAndNextConstNull(nodep->elsesp());
|
|
uint32_t elseCount = m_stackSize;
|
|
if (nodep->branchPred().likely()) elseCount = 0;
|
|
|
|
reset();
|
|
if (ifCount >= elseCount) {
|
|
m_stackSize = savedCount + ifCount;
|
|
if (nodep->elsesp()) nodep->elsesp()->user2(0); // Don't dump it
|
|
} else {
|
|
m_stackSize = savedCount + elseCount;
|
|
if (nodep->thensp()) nodep->thensp()->user2(0); // Don't dump it
|
|
}
|
|
}
|
|
void visit(AstNodeCond* nodep) override {
|
|
if (m_ignoreRemaining) return;
|
|
// Just like if/else above, the ternary operator only evaluates
|
|
// one of the two expressions, so only count the max.
|
|
const VisitBase vb{this, nodep};
|
|
iterateAndNextConstNull(nodep->condp());
|
|
const uint32_t savedCount = m_stackSize;
|
|
|
|
UINFO(8, "?\n");
|
|
reset();
|
|
iterateAndNextConstNull(nodep->thenp());
|
|
const uint32_t ifCount = m_stackSize;
|
|
|
|
UINFO(8, ":\n");
|
|
reset();
|
|
iterateAndNextConstNull(nodep->elsep());
|
|
const uint32_t elseCount = m_stackSize;
|
|
|
|
reset();
|
|
if (ifCount >= elseCount) {
|
|
m_stackSize = savedCount + ifCount;
|
|
if (nodep->elsep()) nodep->elsep()->user2(0); // Don't dump it
|
|
} else {
|
|
m_stackSize = savedCount + elseCount;
|
|
if (nodep->thenp()) nodep->thenp()->user2(0); // Don't dump it
|
|
}
|
|
}
|
|
void visit(AstFork* nodep) override {
|
|
if (m_ignoreRemaining) return;
|
|
const VisitBase vb{this, nodep};
|
|
uint32_t totalCount = m_stackSize;
|
|
VL_RESTORER(m_ignoreRemaining);
|
|
// Sum counts in each statement
|
|
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
|
reset();
|
|
iterateConst(stmtp);
|
|
totalCount += m_stackSize;
|
|
}
|
|
m_stackSize = totalCount;
|
|
}
|
|
void visit(AstNodeCCall* nodep) override {
|
|
if (m_ignoreRemaining) return;
|
|
const VisitBase vb{this, nodep};
|
|
iterateChildrenConst(nodep);
|
|
m_tracingCall = true;
|
|
iterateConst(nodep->funcp());
|
|
UASSERT_OBJ(!m_tracingCall, nodep, "visit(AstCFunc) should have cleared m_tracingCall.");
|
|
}
|
|
void visit(AstCFunc* nodep) override {
|
|
// Don't count a CFunc other than by tracing a call or counting it
|
|
// from the root
|
|
if (!m_tracingCall && !nodep->entryPoint()) return;
|
|
m_tracingCall = false;
|
|
if (nodep->recursive()) return;
|
|
if (!nodep->user2()) { // Short circuit
|
|
VL_RESTORER(m_ignoreRemaining);
|
|
VL_RESTORER(m_stackSize);
|
|
VL_RESTORER(m_inCFunc);
|
|
m_tracingCall = false;
|
|
m_stackSize = 0;
|
|
m_inCFunc = true;
|
|
const VisitBase vb{this, nodep};
|
|
iterateChildrenConst(nodep);
|
|
nodep->user2(m_stackSize + 1);
|
|
}
|
|
m_stackSize += nodep->user2() - 1;
|
|
m_tracingCall = false;
|
|
}
|
|
void visit(AstVar* nodep) override {
|
|
if (!m_inCFunc) return;
|
|
m_stackSize += nodep->isRef() ? sizeof(void*) : nodep->dtypep()->widthTotalBytes();
|
|
iterateChildrenConst(nodep);
|
|
}
|
|
|
|
void visit(AstNodeExpr* nodep) override {} // Short-circuit
|
|
void visit(AstNode* nodep) override {
|
|
if (m_ignoreRemaining) return;
|
|
const VisitBase vb{this, nodep};
|
|
iterateChildrenConst(nodep);
|
|
}
|
|
|
|
VL_UNCOPYABLE(StackCountVisitor);
|
|
};
|
|
|
|
uint64_t V3StackCount::count(AstNode* nodep) {
|
|
const StackCountVisitor visitor{nodep};
|
|
return visitor.stackSize();
|
|
}
|