verilator/src/V3Stats.cpp

183 lines
6.6 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Collect and print statistics
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2005-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
//
//*************************************************************************
#include "V3PchAstMT.h"
#include "V3Stats.h"
#include <iomanip>
#include <map>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Statics
V3Mutex V3Stats::s_mutex;
//######################################################################
// Stats class functions
class StatsVisitor final : public VNVisitorConst {
struct Counters final {
// Nodes of given type
uint64_t m_statTypeCount[VNType::_ENUM_END];
// Nodes of given type with given type immediate child
uint64_t m_statAbove[VNType::_ENUM_END][VNType::_ENUM_END];
// Prediction of given type
uint64_t m_statPred[VBranchPred::_ENUM_END];
};
// STATE
const bool m_fastOnly; // When true, consider only fast functions
const AstNodeExpr* m_parentExprp = nullptr; // Parent expression
Counters m_counters; // The actual counts we will display
Counters m_dumpster; // Alternate buffer to make discarding parts of the tree easier
Counters* m_accump; // The currently active accumulator
std::vector<uint64_t> m_statVarWidths; // Variables of given width
std::vector<std::map<const std::string, uint32_t>>
m_statVarWidthNames; // Var names of given width
// METHODS
void countThenIterateChildren(AstNode* nodep) {
++m_accump->m_statTypeCount[nodep->type()];
iterateChildrenConst(nodep);
}
// VISITORS
void visit(AstVar* nodep) override {
if (nodep->dtypep()) {
if (m_statVarWidths.size() <= static_cast<size_t>(nodep->width())) {
m_statVarWidths.resize(nodep->width() + 5);
if (v3Global.opt.statsVars()) { //
m_statVarWidthNames.resize(nodep->width() + 5);
}
}
++m_statVarWidths.at(nodep->width());
if (v3Global.opt.statsVars()) {
++m_statVarWidthNames.at(nodep->width())[nodep->prettyName()];
}
}
countThenIterateChildren(nodep);
}
void visit(AstNodeExpr* nodep) override {
// Count expression combinations
if (m_parentExprp) ++m_accump->m_statAbove[m_parentExprp->type()][nodep->type()];
VL_RESTORER(m_parentExprp);
m_parentExprp = nodep;
countThenIterateChildren(nodep);
}
void visit(AstNodeIf* nodep) override {
// Track prediction
++m_accump->m_statPred[nodep->branchPred()];
countThenIterateChildren(nodep);
}
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_accump);
if (m_fastOnly && !nodep->slow()) m_accump = &m_counters;
countThenIterateChildren(nodep);
}
void visit(AstNode* nodep) override { countThenIterateChildren(nodep); }
public:
// CONSTRUCTORS
StatsVisitor(AstNetlist* nodep, const std::string& stage, bool fastOnly)
: m_fastOnly{fastOnly}
, m_accump{fastOnly ? &m_dumpster : &m_counters} {
UINFO(9, "Starting stats, fastOnly=" << fastOnly);
memset(&m_counters, 0, sizeof(m_counters));
memset(&m_dumpster, 0, sizeof(m_dumpster));
iterateConst(nodep);
// Shorthand
const auto addStat = [&](const std::string& name, double count, unsigned precision = 0) {
V3Stats::addStat(stage, name, count, precision);
};
// Variable widths
for (unsigned i = 0; i < m_statVarWidths.size(); i++) {
if (const uint64_t count = m_statVarWidths.at(i)) {
std::stringstream ss;
ss << "Vars, width " << std::setw(5) << std::dec << i;
const std::string prefix = ss.str();
if (v3Global.opt.statsVars()) {
for (const auto& it : m_statVarWidthNames.at(i)) {
addStat(prefix + " " + it.first, it.second);
}
} else {
addStat(prefix, count);
}
}
}
// Node types (also total memory usage)
const auto typeName = [](int type) { return std::string{VNType{type}.ascii()}; };
const auto typeSize = [](int type) { return VNType{type}.typeInfo()->m_sizeof; };
size_t totalNodeMemoryUsage = 0;
for (int t = 0; t < VNType::_ENUM_END; ++t) {
if (const uint64_t count = m_counters.m_statTypeCount[t]) {
totalNodeMemoryUsage += count * typeSize(t);
addStat("Node count, " + typeName(t), count);
}
}
addStat("Node memory TOTAL (MiB)", totalNodeMemoryUsage >> 20);
// Node Memory usage
for (int t = 0; t < VNType::_ENUM_END; ++t) {
if (const uint64_t count = m_counters.m_statTypeCount[t]) {
const double share = 100.0 * count * typeSize(t) / totalNodeMemoryUsage;
addStat("Node memory share (%), " + typeName(t), share, 2);
}
}
// Expression combinations
for (int t1 = 0; t1 < VNType::_ENUM_END; ++t1) {
for (int t2 = 0; t2 < VNType::_ENUM_END; ++t2) {
if (const uint64_t c = m_counters.m_statAbove[t1][t2]) {
addStat("Expr combination, " + typeName(t1) + " over " + typeName(t2), c);
}
}
}
// Branch predictions
for (int t = 0; t < VBranchPred::_ENUM_END; ++t) {
if (const uint64_t c = m_counters.m_statPred[t]) {
addStat("Branch prediction, "s + VBranchPred{t}.ascii(), c);
}
}
}
};
//######################################################################
// Top Stats class
void V3Stats::addStatSum(const string& name, double count) VL_MT_SAFE_EXCLUDES(s_mutex) {
V3LockGuard lock{s_mutex};
addStat(V3Statistic{"*", name, count, 0, true});
}
void V3Stats::statsStageAll(AstNetlist* nodep, const std::string& stage, bool fastOnly) {
StatsVisitor{nodep, stage, fastOnly};
}
void V3Stats::statsFinalAll(AstNetlist* nodep) { statsStageAll(nodep, "Final"); }